Built with R version:
3.5.0


Built with R version:
3.5.0

Libraries

Load necessary libraries

library(affy)
Loading required package: BiocGenerics
Loading required package: parallel

Attaching package: ‘BiocGenerics’

The following objects are masked from ‘package:parallel’:

    clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport, clusterMap, parApply, parCapply, parLapply, parLapplyLB, parRapply, parSapply,
    parSapplyLB

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, append, as.data.frame, basename, cbind, colMeans, colnames, colSums, dirname, do.call, duplicated, eval, evalq, Filter, Find, get, grep, grepl,
    intersect, is.unsorted, lapply, lengths, Map, mapply, match, mget, order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank, rbind, Reduce, rowMeans,
    rownames, rowSums, sapply, setdiff, sort, table, tapply, union, unique, unsplit, which, which.max, which.min

Loading required package: Biobase
Welcome to Bioconductor

    Vignettes contain introductory material; view with 'browseVignettes()'. To cite Bioconductor, see 'citation("Biobase")', and for packages 'citation("pkgname")'.
library(ComplexHeatmap)
Loading required package: grid
========================================
ComplexHeatmap version 1.18.1
Bioconductor page: http://bioconductor.org/packages/ComplexHeatmap/
Github page: https://github.com/jokergoo/ComplexHeatmap
Documentation: http://bioconductor.org/packages/ComplexHeatmap/

If you use it in published research, please cite:
Gu, Z. Complex heatmaps reveal patterns and correlations in multidimensional 
  genomic data. Bioinformatics 2016.
========================================
library(plot3D)
library(gplots)

Attaching package: ‘gplots’

The following object is masked from ‘package:stats’:

    lowess
library(circlize)
========================================
circlize version 0.4.4
CRAN page: https://cran.r-project.org/package=circlize
Github page: https://github.com/jokergoo/circlize
Documentation: http://jokergoo.github.io/circlize_book/book/

If you use it in published research, please cite:
Gu, Z. circlize implements and enhances circular visualization 
  in R. Bioinformatics 2014.
========================================
library(AnnotationDbi)
Loading required package: stats4
Loading required package: IRanges
Loading required package: S4Vectors

Attaching package: ‘S4Vectors’

The following object is masked from ‘package:gplots’:

    space

The following object is masked from ‘package:base’:

    expand.grid
library(limma)

Attaching package: ‘limma’

The following object is masked from ‘package:BiocGenerics’:

    plotMA
library(lattice)
library(org.Hs.eg.db)
library(MASS)

Attaching package: ‘MASS’

The following object is masked from ‘package:AnnotationDbi’:

    select
library(RColorBrewer)
library(AnnotationDbi)
library(rglwidget)
The functions in the rglwidget package have been moved to rgl.
###library(hgu133plus2hsentrezgcdf)
library(VennDiagram)
Loading required package: futile.logger
library(org.Hs.eg.db)
library(GenomicRanges)
Loading required package: GenomeInfoDb
library(GenomicFeatures)
library(rtracklayer)
library(biomaRt)
library(glmnet)
Loading required package: Matrix

Attaching package: ‘Matrix’

The following object is masked from ‘package:S4Vectors’:

    expand

Loading required package: foreach
Loaded glmnet 2.0-16
library(survival)
library(Hmisc)
Loading required package: Formula
Loading required package: ggplot2

Attaching package: ‘Hmisc’

The following object is masked from ‘package:AnnotationDbi’:

    contents

The following object is masked from ‘package:Biobase’:

    contents

The following objects are masked from ‘package:base’:

    format.pval, units
library(ConsensusClusterPlus)
library(pheatmap)
library(ggplot2)
library(heatmap.plus)
library(rgl)
library(caret)

Attaching package: ‘caret’

The following object is masked from ‘package:survival’:

    cluster
library(e1071)

Attaching package: ‘e1071’

The following object is masked from ‘package:Hmisc’:

    impute
library(tmod)
set1 = c(brewer.pal(9,"Set1"), brewer.pal(8, "Dark2"))
violinJitter <- function(x, magnitude=1){
  d <- density(x)
  data.frame(x=x, y=runif(length(x),-magnitude/2, magnitude/2) * approxfun(d$x, d$y)(x))
}
rotatedLabel <- function(x0 = seq_along(labels), y0 = rep(par("usr")[3], length(labels)), labels, pos = 1, cex=1, srt=45, ...) {
  w <- strwidth(labels, units="user", cex=cex)
  h <- strheight(labels, units="user",cex=cex)
  u <- par('usr')
  p <- par('plt')
  f <- par("fin")
  xpd <- par("xpd")
  par(xpd=NA)
  text(x=x0 + ifelse(pos==1, -1,1) * w/2*cos(srt/360*2*base::pi), y = y0 + ifelse(pos==1, -1,1) * w/2 *sin(srt/360*2*base::pi) * (u[4]-u[3])/(u[2]-u[1]) / (p[4]-p[3]) * (p[2]-p[1])* f[1]/f[2] , labels, las=2, cex=cex, pos=pos, adj=1, srt=srt,...)
  par(xpd=xpd)
}
avefc = function (y, log=TRUE, replace= FALSE) {
     if (log) y = 2^y
   if (replace) y = y + (1-min(y))
   m = apply(y,1,mean)
     y.n = y/m  
     y.n2 = y.n
     y.n2 [y.n2 < 1] = 1/ (y.n2 [y.n2 < 1])
     ave.fc = apply (y.n2, 1, mean)
     return(ave.fc)
     }

Ensembl Library

For gene convertion from array to HUGO

ensembl = useMart( "ensembl", dataset = "hsapiens_gene_ensembl" )

Gene Expression Data

Upload or generate GEP normalized matrix

### choice 1: import processed matrix
# data.dir="./Rmd.files/"
data.dir = '/Users/emagene/Dropbox/codes/github/PTCL/'
setwd(data.dir)
load (file.path(data.dir,"/Rmd.files/541_PTCL_batch_adjusted_geo.id.Rdata"))
geneExpr = adj.data
# import batch and re-order accordingly
load(file.path(data.dir,"/Rmd.files/PTCL.batch.Rdata"))
batch = batch [order(batch$nameNEW),]
batch.series = as.vector(batch$center)
batch$cancer = "cancer"
# ### OPTIONAL: CHECK BATCH ON FINAL.MOLEC
# 
# #mod = model.matrix(~as.factor(center), data=batch)
# mod = model.matrix(~as.factor(final.molec), data=design)
# mod0 = model.matrix(~1, data= batch)
# library(sva)
# n.sv = num.sv(adj.data,mod,method="leek")
# svobj = sva(adj.data,mod,mod0,n.sv=n.sv)
# 
# pValues = f.pvalue(adj.data,mod,mod0)
# qValues = p.adjust(pValues,method="BH")
# modSv = cbind(mod,svobj$sv)
# mod0Sv = cbind(mod0,svobj$sv)
# pValuesSv = f.pvalue(adj.data,modSv,mod0Sv)
# qValuesSv = p.adjust(pValuesSv,method="BH")
### end of choice 1
### choice 2: generate your own affy object and custom data
# download CEL files from GEO series GSE6338, GSE19067, GSE19069, GSE40160, GSE58445, GSE65823 and EBI series ETABM702, ETABM783
# GSM368580.CEL, GSM368582.CEL, GSM368584.CEL, GSM368586.CEL, GSM368589.CEL, GSM368591.CEL, GSM368594.CEL, GSM472164.CEL, GSM1411278.CEL, GSM1411284.CEL, GSM1411285.CEL, GSM1411287.CEL, GSM1411355.CEL, GSM1411364.CEL, GSM1411368.CEL, GSM1411425.CEL, GSM1411427.CEL excluded from the analysis (see Methods for explaination")
### celfiles <- dir("~/Documents/DATI/PTCL.nos/GSE6338-GSE19067-GSE19069-GSE40160-GSE58445-GSE65823-ETABM702-ETABM783/", pattern = ".CEL")
### library(affy)
### gset = justRMA(celfile.path = "/Users/emagene/Documents/DATI/PTCL.nos/GSE6338-GSE19067-GSE19069-GSE40160-GSE58445-GSE65823-ETABM702-ETABM783/", ### filenames = celfiles, sampleNames = gsub(".CEL","", celfiles), cdfname = "hgu133plus2hsentrezgcdf")
### geneExpr = exprs(gset)
### batch adjustment
### library(sva)  
### # import batch and re-order accordingly
### load("./Rmd.files/PTCL.batch.Rdata")
### batch = batch [order(rownames(batch)),]
### batch.series = as.vector(batch$center)
### geneExprNEW = geneExpr [ , order(colnames(geneExpr)) ]
### geneExprNEW = geneExprNEW[grep("AFFX",rownames(geneExprNEW), invert=TRUE),]
### # check order correspondence and, if correct, adjust data
### if (all(colnames(geneExprNEW) == rownames(batch))) {
###   adj.data = ComBat (geneExprNEW, batch.series, mod = NULL, par.prior = TRUE, prior.plots = TRUE)
### } else {
###   cat("Error: colnames and batch did not correspond")
### }
### geneExpr = adj.data
### colnames(geneExpr) = as.vector(batch$nameNEW)
### end of choice 2

Clinical Data

Upload paz info with clinical and mutational data

pts.info.data <- read.table("./Rmd.files/541_paz_info_MUT.txt", sep="\t", header=TRUE, check.names=FALSE, stringsAsFactors = F)
# customize colors for categories
levels(as.factor(pts.info.data$final.molec))
 [1] "AITL"     "ALCL.neg" "ALCL.pos" "ATLL"     "NKT"      "PTCL.nos" "T.CD30"   "T.CD4"    "T.CD8"    "T.DR"     "T.reg"    "TCR-HL"  
# "AITL"     "ALCL.neg" "ALCL.pos" "ATLL"     "NKT"      "PTCL.nos" "T.CD30"   "T.CD4"    "T.CD8"    "T.DR"     "T.reg"    "TCR-HL"  
colorz = c("black", "yellow","dodgerblue2","brown2","darkorchid1", "orange", "grey42", "grey52","grey62","grey72","grey82","grey92")
temp = split (  pts.info.data$sample.nameNEW, pts.info.data$final.molec )
colorx = colnames(geneExpr)
length(colorz)
[1] 12
length(temp)
[1] 12
for (i in 1:length(colorz)) colorx [ which(colorx %in% unlist(temp[i])) ] = colorz[i]
library(gplots)
colorx = col2hex(colorx)
### build design matrix and transform to numerical 
design <- pts.info.data[,c(1:2,6:8,14:17)]
rownames(design)<- design[,1]
design<- design[,-c(1:2)]
#design<-na.omit(design) ### select onyl patients with all mutations data available (n=53)
design$age<- as.numeric(as.character(design$age))
design$age<- design$age - median(design$age)
design[design == "WT"] <- 0
design[design == "MUT"] <- 1
design$final.molec[design$final.molec=="AITL"] <- 0
design$final.molec[design$final.molec=="PTCL.nos"] <- 1
design$final.molec[design$final.molec=="ALCL.neg"] <- 2
design$final.molec[design$final.molec=="ALCL.pos"] <- 3
design$final.molec[design$final.molec=="ATLL"] <- 4
design$final.molec[design$final.molec=="NKT"] <- 5
design$final.molec[477:541] <- 6
design$gender[design$gender=="M"] <- 1
design$gender[design$gender=="F"] <- 0
design$age = NULL
all(pts.info.data$sample.nameNEW == batch$nameNEW) 
[1] TRUE

Pie Chart with Percentages

slices <- table(pts.info.data$final.molec)
lbls <- names(table(pts.info.data$final.molec))
pct <- round(slices/sum(slices)*100)
lbls <- paste(lbls, ": ", slices, " (", pct, "%)", sep="" ) # add percents to labels
#pdf("Figure_1a_pie_plot.pdf", width = 5, height = 5)
par(mfrow=c(1,1))
par(mar=c(3,3,3,3), xpd=F)
pie(slices,labels = lbls, init.angle = 0, col=colorz, main="", cex=0.6, radius=0.8)

#dev.off()

PCA

# apply variational filter
afc2 = avefc(geneExpr, log=TRUE, replace=FALSE)
data541exprs.vf = geneExpr [afc2 >= 2, ]
dim(data541exprs.vf )
[1] 1840  541
# retry PCA on shorted gene list
data541m = t(as.matrix(data541exprs.vf))
pca<-prcomp(data541m,scale=T)
mfrow3d(nr = 1, nc = 1, sharedMouse = T)  
plot3d(pca$x,rgl.use=F,col=colorx,size=0.6,type="s")
rglwidget()

Heatmap

mat = as.matrix(data541exprs.vf)
base_mean = rowMeans(mat)
mat_scaled = t(apply(mat, 1, scale))
types = pts.info.data$final.molec
color.annot = col2hex(colorz); names (color.annot)= names(temp)  
ha = HeatmapAnnotation(df = data.frame(type = types) , col = list(type = c( color.annot ) ) )
ha@anno_list[[1]]@color_mapping@colors = col2hex(colorz)
names(ha@anno_list[[1]]@color_mapping@colors) = names(temp)
ht = Heatmap(mat_scaled, name = "expression", km = 7, clustering_method_columns = "ward.D", col = colorRamp2(c(-1, 0, 1), c("green", "white", "red")), top_annotation = ha, top_annotation_height = unit(4, "mm"), show_row_names = FALSE, show_column_names = FALSE)
column_order(ht)
  [1]  91 236  85  86 429  88  64 311 270 271 229 231 180 257  87 227 258 304 309 436 431 234 148 147  31  29 450 281 500 521 499 273 272 269 522 520 501 519 498 518 497 505 506 504 486
 [46] 485 482 483 484 502 503 524 527 529 528 530 532 531 525 526 523 434  61 182 176  63  83  66 445 230 515 517 513 296 298 516 494 492 496 493 314 297 294 295 293 289 292 512 507 511
 [91] 510 509 291 290 514 508 448 447 444 440 396 397 395 394 391  50  51 187 192 433 349 363 385 428 435 427 389 366 491 401 334 459 392 402 388 457 463 446 380 351 474 372 376 489 487
[136] 537 488 534 536 495 490 535 533 481 479 478 480 477 264 261 268 259 262 253 267 265 266 263  65 254 256 255 318 308 320 319 315 324 326 313 280 299 285 316 286 278 344 330 430 331
[181] 275 305 307 136 426 184 162 167 422 421 469 341 245 160 179 153 443 449 181 149 368 186 164 141 194 218 161  98 300 312  84 442 323 301 174 306 248 329 325 178 139 145 142 144 185
[226] 157 249 172 177 158 183 150 170 171 152 131 134 137 216 214 213 241 202 205 203 191 130 133 195 215 244 211 246 238 197 224 226 225 169 538 168 539 540 219 223 220 221 222 206 204
[271] 232 242 200 243 143 250 154 207 198 235 240 247 252 163 155 383 188 239 417 276 406  93 303 339 109  53 124  82  26  32  23  22  20 189  18 398  41  44  10 193 217 129   2 140 208
[316] 251 199 201 209 210 212 233 237 288 284 420 439 274 352 287 441 328 283 282 321 317 310 279 322 332 302 348 419 399 387 166 159 337 467 151 277 355 359 410 353 470 475 466 175 370
[361]  42 411 374 393 404 327 456  94 432 541 156 173 146 165 135 128 106 458 361 364 354  90 454  27 403 138 196 462 453 338 415 407 379 371 464 461 110  79 121  45 405 425  96 102 424
[406] 423 360 452 451 365 358  89   1  28  19 347 101 260 455  59 418 367  48  69 190 333 132  17 378 350 468 346   9 414 409 413 412 408 116  70 122 123 340 381 460 382  77  38 108  37
[451] 228  49  68  67 416  62  92  71   4 103 126  36 120 127  56  74  13  12  72 104 105 100  21  30  25  33  24 465 345 117 343 342 471 336 476 356  15  60 390 357 362  99  97 437  39
[496]  78 438  80 375  40 113 112  52  58 400  57  55  54  76  11 114  73  75 472   3   6 377 119  47  35 125   5  95  81  34  46  43  16  14 386   8 473 335 111 384   7 118 373 369 107
[541] 115

Check relative log expression after batch correction

rle.custom = function (a, logged2 = TRUE, file = NULL, colorbox= NULL, labels=NULL , legend = NULL ) {
    a.m <- apply(a,1,median)
if (logged2) {
    for (i in 1:dim(a)[2]) {
         a [,i] <-  a [,i] - a.m
    }
    } else {
        for (i in 1:dim(a)[2]) {
         a [,i] <-  log (a [,i] / a.m )
    }
    }
   # png(file,10240,3840)
    par(mar=c(10,4,6,2))
    boxplot (a, ylim= c(-5,5), outline=F, col=colorbox, xlab="pts", names=labels, las=2, cex.axis = 1.5, main="RLE", xlim = c(1,600), cex.main = 5 )
    legend("bottomright",legend = c(levels(as.factor(pts.info.data$final.molec))),   
      fill = colorz, # 6:1 reorders so legend order matches graph
      title = "Legend",
      cex = 5)
  #  dev.off()
    a.c = apply(a, 2, stats::quantile)
    return(a.c)
}
#rle.medians = rle.custom(geneExpr, colorbox=colorx, file="./RLE.541.png", labels=pts.info.data$sample.nameNEW )
#plot(rle.medians[3,], type="l", xlab="pts", ylab="RLE median" )
rle.medians = rle.custom(geneExpr, colorbox=colorx, file="./RLE.541.png", labels=pts.info.data$sample.nameNEW )

plot(rle.medians[3,], type="l", xlab="pts", ylab="RLE median" )

Final Gene Expression Matrix

Define design file and filter geneExpr for patients included in design data frame and

design <- pts.info.data[,c(1:2,6:8,14:17)]
rownames(design)<- design[,1]
design<- design[,-c(1:2)]
design<-na.omit(design) ###  select onyl patients with all mutations data available (n=53)
design$age<- as.numeric(as.character(design$age))
design$age<- design$age - median(design$age)
design[design == "WT"] <- 0
design[design == "MUT"] <- 1
design$final.molec[design$final.molec=="AITL"] <- 0
design$final.molec[design$final.molec=="PTCL.nos"] <- 1
design$gender[design$gender=="M"] <- 1
design$gender[design$gender=="F"] <- 0
design$offset <- rep(1, nrow(design))
design<-design[,c(8,1:7)]
all(pts.info.data$sample.nameNEW == colnames(geneExpr)) ## check correspondence
[1] TRUE
# geneExpr = geneExpr [ , order (pts.info.data$geo.id)] ### do only to set correspondence in case of custom procedure
# colnames(geneExpr) = pts.info.data$sample.nameNEW [ order (pts.info.data$geo.id)]
geneExpr2<- (geneExpr[, rownames(design)])
geneExpr2<- data.matrix(geneExpr2, rownames.force = NA)
design<- data.matrix(design, rownames.force = NA)

Model fitting

We use the lmFit function from the limma package. This comes with a whole series of powerful and reliable tests.

glm = lmFit(geneExpr2[,rownames(design)], design = design )
glm = eBayes(glm)
F.stat <- classifyTestsF(glm[,-1],fstat.only=TRUE)
glm$F <- as.vector(F.stat)
df1 <- attr(F.stat,"df1")
df2 <- attr(F.stat,"df2")
if(df2[1] > 1e6){
  glm$F.p.value <- pchisq(df1*glm$F,df1,lower.tail=FALSE)
}else
  glm$F.p.value <- pf(glm$F,df1,df2,lower.tail=FALSE)
set.seed(12345678)
rlm <- lmFit(geneExpr[,rownames(design)], apply(design, 2, sample))
rlm <- eBayes(rlm)
F.stat <- classifyTestsF(rlm[,-1],fstat.only=TRUE)
rlm$F <- as.vector(F.stat)
df1 <- attr(F.stat,"df1")
df2 <- attr(F.stat,"df2")
if(df2[1] > 1e6){
  rlm$F.p.value <- pchisq(df1*rlm$F,df1,lower.tail=FALSE)
}else
  rlm$F.p.value <- pf(rlm$F,df1,df2,lower.tail=FALSE)
F.stat <- classifyTestsF(glm[,2:5],fstat.only=TRUE)
df1 <- attr(F.stat,"df1")
df2 <- attr(F.stat,"df2")
F.p.value <- pchisq(df1*F.stat,df1,lower.tail=FALSE)
R.stat <- classifyTestsF(rlm[,2:5],fstat.only=TRUE)
Rall = 1 - 1/(1 + glm$F * (ncol(design)-1)/(nrow(design)-ncol(design)))
Rgenetics = 1 - 1/(1 + F.stat * 4/(nrow(design)-ncol(design)))
Pgenetics = 1 - 1/(1 + R.stat * 4/(nrow(design)-ncol(design)))
names(Rgenetics) <- names(Pgenetics) <- names(Rall) <-  rownames(geneExpr)

Differentially Expressed Genes

par(bty="n", mgp = c(2,.33,0), mar=c(3,2.5,1,1)+.1, las=1, tcl=-.25, xpd=NA)
d <- density(Pgenetics,bw=1e-3)
f <- 40 #nrow(gexpr)/512
#pdf("Figure_2a_MAY.pdf", width = 10, height = 7)
par(mfrow=c(1,1))
par(mar=c(8,5,5,5), xpd=F)
plot(d$x, d$y * f, col='grey', xlab=expression(paste("Explained variance per gene ", R^2)), main="", lwd=2, type="l", ylab="", xlim=c(0,1), cex.axis=1.2, cex.lab=1.5, bty="n")
title(ylab="Density", line=2.5, cex.lab=1.5)
d <- density(Rgenetics, bw=1e-3)
r <- min(Rgenetics[p.adjust(F.p.value,"BH")<0.01]) ######## threshold to select 412 genes
x0 <- which(d$x>r)
polygon(d$x[c(x0[1],x0)], c(0,d$y[x0])* f, col=paste(set1[1],"44",sep=""), border=NA)
lines(d$x, d$y* f, col=set1[1], lwd=2)
text(d$x[x0[1]], d$y[x0[1]]*f +20, pos=4, paste(sum(Rgenetics > r), "genes q < 0.01"))
legend("topright", bty="n", col=c(set1[1], "grey"), lty=1, c("Observed","Random"), lwd=2)

#dev.off()
glmPrediction <- glm$coefficients %*% t(design)
rlmPrediction <- rlm$coefficients %*% t(design)

Print signficiant genes

kk<-as.data.frame((p.adjust(F.p.value,"BH")<0.01))
kk$gene<- rownames(kk)
colnames(kk)[1]<-"code"
kk2<-kk[kk$code=="TRUE",]
### sort(kk2$gene) ##### if you want to print the entire list of differentially expressed genes

Significant effects per covariate

Extract the list of differentially expressed genes by mutations

### customize colors in colMutations
# colMutations = c(brewer.pal(8,"Set1")[-6], rev(brewer.pal(8,"Dark2")), brewer.pal(7,"Set2"))[c(1:12,16:19,13:15)]
# o <- order(apply(col2rgb(colMutations),2,rgb2hsv)[1,])
# colMutations <- colMutations[rev(o)][(4*1:19 +15) %% 19 + 1][1:7]
colMutations = col2hex(c("magenta", "purple","gray60","red","lightblue","green","orange"))
names(colMutations) <- colnames(design)[-1]
gene_code<- kk2$gene
tab=NULL
for(i in (1:length(kk2$gene)))
{
  gene_single<- gene_code[i]
  y <- glm$coefficients[gene_single,-1]+glm$coefficients[gene_single,1]
  w <- glm$p.value[gene_single,-1] < 0.05
  int<-c(gene_single, as.character(w))
  tab<- rbind(tab, int)
}
rownames(tab)<-seq(1:nrow(tab))
colnames(tab)<- c("gene",colnames(design)[-1])
# Write to disk a file with all significant genes
#write.table(tab, "table_differentially_expressed_gene.txt",sep="\t", quote=F, row.names = F, col.names = T)

Example of extraction

 # temp_name = unique(getBM( attributes = c("ensembl_transcript_id", "entrezgene", "external_gene_name"), filters = "entrezgene", values = gsub("_at","",gene_single),
 # mart = ensembl)$external_gene_name)
  #pdf("Figure_2b.pdf", width = 10, height = 7)
  par(mfrow=c(1,1))
  par(mar=c(10,8,5,5), xpd=F)
  par(bty="n", mgp = c(1.5,.33,0),las=1, tcl=-.25, xpd=F)
  temp_name<- "YAP1"
  plot(glmPrediction[gene_single,], geneExpr[gene_single,rownames(design)], ylab="", xlab="",
       pch=16, cex=1, cex.axis=1.2, cex.lab=1.5)
  title(ylab=(paste("Observed ",temp_name, " expression")), line=2.5, cex.lab=1.5)
   title( xlab=(paste("Predicted ",temp_name, " expression")), line=2.5, cex.lab=1.5)
  abline(0,1)
  u <- par("usr")
  par(xpd=NA)
  y <- glm$coefficients[gene_single,-1]+glm$coefficients[gene_single,1]
  u <- par("usr")
  x0 <- rep(u[3]+1,ncol(design)-1)
  y0 <- u[4] + 0.05*(u[4]-u[3]) - rank(-y)/length(y) * (u[4]-u[3])/1.2
  d <- density(y)
  lines(d$x, d$y/5+1+u[3], col="grey")
  lines(d$x, -d$y/5+1+u[3], col="grey")
points(x=y, y=x0+violinJitter(y, magnitude=0.25)$y, col=colMutations, pch=16, cex=1.5)
  text(x=glm$coefficients[gene_single,1], y= 5.2, "Model coefficients", cex=0.8)
legend("topleft",names(colMutations), col = colMutations, bty= "n", cex = 1.2, pch = 16)

#dev.off()

Plot significant effects per covariate (q<0.01)

testResults <- decideTests(glm, method="hierarchical",adjust.method="BH", p.value=0.01)[,-1]
significantGenes <- sapply(1:ncol(testResults), function(j){
  c <- glm$coefficients[testResults[,j]!=0,j+1]
  table(cut(c, breaks=c(-5,seq(-1.5,1.5,l=7),5)))
})
colnames(significantGenes) <- colnames(testResults)
rownames(tab)<-c(1:nrow(tab))
tab2<- as.data.frame(tab)
tab2$gene<-as.character(as.character(tab2$gene))
tab2$final.molec<-as.character(as.character(tab2$final.molec))
tab2$TET2<-as.character(as.character(tab2$TET2))
tab2$RHOA<-as.character(as.character(tab2$RHOA))
tab2$IDH2<-as.character(as.character(tab2$IDH2))
tab2$DNMT3A<-as.character(as.character(tab2$DNMT3A))
#  pdf("Figure_2c.pdf", width = 10, height = 7)
  par(mfrow=c(1,1))
  par(mar=c(8,8,5,5), xpd=F)
par(mfrow=c(1,1))
par(bty="n", mgp = c(2.5,.33,0), mar=c(5,5.5,5,0)+.1, las=2, tcl=-.25)
b <- barplot(significantGenes, las=2, ylab = "Differentially expressed genes", col=brewer.pal(8,"RdYlBu"), legend.text=FALSE , border=0, xaxt="n", cex.lab=1.5)#, col = set1[simple.annot[names(n)]], border=NA)
rotatedLabel(x0=b-0.1, y0=rep(-0.5, ncol(significantGenes)), labels=colnames(significantGenes), cex=1.2, srt=45, font=ifelse(grepl("[[:lower:]]", colnames(design))[-1], 1,3), col=colMutations)
rotatedLabel(b-0.1, colSums(significantGenes), colSums(significantGenes), pos=3, cex=, srt=45)#dev.off()
clip(0,30,0,1000)
x0 <- 7.5
image(x=x0+c(0,0.8), y=par("usr")[4]+seq(-1,1,l=9) -4, z=matrix(1:8, ncol=8), col=brewer.pal(8,"RdYlBu"), add=TRUE)
text(x=x0+1.1, y=par("usr")[4]+c(-1,0,1) -4, format(seq(-1,1,l=3),2), cex=0.66)
lines(x=rep(x0+0.9, 2), y=par("usr")[4]+c(-1,1) -4)
segments(x0+0.9,par("usr")[4] + 1-4,x0+0.95,par("usr")[4] + 1-4)
segments(x0+0.9,par("usr")[4] + 0-4,x0+0.95,par("usr")[4] + 0-4)
segments(x0+0.9,par("usr")[4] + -1-4,x0+0.95,par("usr")[4] + -1-4)
text(x0 + 0.45, par("usr")[4] + 1.5-4, "log2 FC", cex=.66)

#dev.off()
# par(bty="n", mgp = c(2.5,.33,0), mar=c(3,3.3,3,0)+.1, las=1, tcl=-.25)
# t <- table(rowSums(abs(testResults[,1:6])))
# b <- barplot(t[-1],ylab="Differentially expressed genes", col=rev(brewer.pal(7, "Spectral")[-(4:5)]), border=NA)
# rotatedLabel(b-0.1, t[-1], t[-1], pos=3, cex=1, srt=45)
# title(xlab="Associated drivers", line=2)

Print the list of differently expressed genes using the Ensembl annotation

select_hist<- pts.info.data[pts.info.data$final.molec == "AITL" |  pts.info.data$final.molec == "PTCL.nos",]
gene<- as.data.frame(testResults)
sig_genes<- gene[gene$final.molec!= 0 |gene$IDH2 != 0 | gene$TET2 != 0 | gene$DNMT3A != 0 | gene$RHOA != 0,]
list_genes<-sort(rownames(sig_genes)) ##### list of signficiant genes
geneannotation1 <- getBM( attributes = c("ensembl_transcript_id", "entrezgene", "external_gene_name"), filters = "entrezgene", values = gsub("_at","",list_genes), mart = ensembl)
sort(unique(geneannotation1$external_gene_name))
 [1] "ADRA2A"     "AL441992.1" "ARHGEF10"   "C3"         "COL4A4"     "DZIP1"      "EFNB2"      "HS3ST3A1"   "ID2"        "NETO2"      "OSMR"       "PRRX1"      "ROBO1"     
[14] "SLC5A3"     "XKR4"       "YAP1"      

Generate a heatmap with AITL, PTCL-NOS with the extracted differentially expressed genes.

gep<- geneExpr[,select_hist$sample.nameNEW]
mat<- gep[list_genes,]
setdiff(rownames(mat), paste0(unique(geneannotation1$entrezgene),"_at"))
character(0)
for (ii in 1:nrow(mat)) {
  #if(length (which (paste0(unique(geneannotation1$entrezgene),"_at") == rownames(mat)[ii])) != 0 ) rownames(mat) [ii] = geneannotation1$external_gene_name [ which (paste0(unique(geneannotation1$entrezgene),"_at") == rownames(mat)[ii])]
  rownames(mat) [ii] = unique(geneannotation1$external_gene_name) [ which (paste0(unique(geneannotation1$entrezgene),"_at") == rownames(mat)[ii])]
}
mycol= c("red","white","yellow")
mylabel = select_hist[,c("sample.nameNEW","final.molec","IDH2","RHOA","TET2","DNMT3A")]
rownames(mylabel) = mylabel$sample.nameNEW
mylabel$sample.nameNEW = NULL
mylabel.nocol = mylabel
mylabel.col = mylabel
mylabel.col[is.na(mylabel.col)]<-0
#head(mylabel.col)
mylabel.col$final.molec[mylabel.col$final.molec == "AITL"] = "black"; mylabel.col$final.molec[mylabel.col$final.molec == "PTCL.nos"] = "orange"
for (a in 2:5) mylabel.col[,a] = factor(mylabel.col[,a], levels = levels(as.factor(mylabel.col[,a])), labels = mycol )
mat  <- mat - rowMeans(mat)
par(mfrow=c(1,1))
cluster.pts.nr = pheatmap(mat, annotation_col = mylabel.nocol, annotation_colors = list(final.molec = c(AITL = "black", PTCL.nos = "orange"), filename= "x.pdf",
                                  IDH2 = c(MUT=mycol[1],"NA"=mycol[2],WT=mycol[3]),
                                  RHOA = c(MUT=mycol[1],"NA"=mycol[2],WT=mycol[3]),
                                  TET2 = c(MUT=mycol[1],"NA"=mycol[2],WT=mycol[3]),
                                  DNMT3A = c(MUT=mycol[1],"NA"=mycol[2],WT=mycol[3]) ) , show_colnames = F, cellheight = 15, 
         border_color= NA, color = colorRampPalette(rev(brewer.pal(n = 5 , name = "RdYlBu")))(20), scale = "row", clustering_method = "ward.D2",clustering_distance_cols = "euclidean" , silent = F)

### export pts order
cluster.pts.nr$tree_col$labels [cluster.pts.nr$tree_col$order]
  [1] "PTCL.nos..23"  "PTCL.nos..428" "PTCL.nos..448" "PTCL.nos..124" "PTCL.nos..247" "PTCL.nos..463" "PTCL.nos..89"  "PTCL.nos..156" "PTCL.nos..432" "PTCL.nos..216" "PTCL.nos..25" 
 [12] "PTCL.nos..87"  "PTCL.nos..94"  "PTCL.nos..98"  "PTCL.nos..105" "PTCL.nos..93"  "PTCL.nos..195" "AITL..413"     "PTCL.nos..531" "PTCL.nos..143" "PTCL.nos..46"  "PTCL.nos..28" 
 [23] "PTCL.nos..185" "PTCL.nos..416" "PTCL.nos..112" "PTCL.nos..424" "PTCL.nos..134" "PTCL.nos..32"  "PTCL.nos..22"  "PTCL.nos..194" "PTCL.nos..30"  "PTCL.nos..211" "PTCL.nos..52" 
 [34] "PTCL.nos..97"  "PTCL.nos..201" "PTCL.nos..27"  "PTCL.nos..68"  "PTCL.nos..139" "PTCL.nos..72"  "PTCL.nos..120" "PTCL.nos..444" "PTCL.nos..24"  "PTCL.nos..15"  "PTCL.nos..109"
 [45] "PTCL.nos..29"  "PTCL.nos..100" "PTCL.nos..171" "PTCL.nos..104" "PTCL.nos..99"  "PTCL.nos..126" "PTCL.nos..258" "AITL..536"     "PTCL.nos..535" "PTCL.nos..20"  "PTCL.nos..102"
 [56] "PTCL.nos..452" "PTCL.nos..529" "PTCL.nos..90"  "PTCL.nos..230" "PTCL.nos..231" "PTCL.nos..232" "PTCL.nos..236" "PTCL.nos..16"  "PTCL.nos..189" "PTCL.nos..506" "PTCL.nos..519"
 [67] "PTCL.nos..151" "PTCL.nos..186" "AITL..419"     "PTCL.nos..455" "PTCL.nos..47"  "PTCL.nos..213" "PTCL.nos..161" "PTCL.nos..61"  "PTCL.nos..209" "PTCL.nos..119" "PTCL.nos..80" 
 [78] "PTCL.nos..34"  "PTCL.nos..440" "AITL..19"      "AITL..18"      "PTCL.nos..504" "PTCL.nos..118" "PTCL.nos..293" "PTCL.nos..92"  "PTCL.nos..251" "PTCL.nos..101" "PTCL.nos..446"
 [89] "AITL..479"     "PTCL.nos..469" "AITL..411"     "AITL..473"     "AITL..472"     "AITL..481"     "AITL..487"     "PTCL.nos..434" "PTCL.nos..180" "PTCL.nos..135" "PTCL.nos..445"
[100] "PTCL.nos..408" "PTCL.nos..409" "PTCL.nos..460" "PTCL.nos..468" "PTCL.nos..470" "PTCL.nos..471" "PTCL.nos..441" "PTCL.nos..451" "AITL..12"      "PTCL.nos..237" "AITL..165"    
[111] "PTCL.nos..178" "AITL..458"     "AITL..191"     "AITL..163"     "AITL..187"     "AITL..62"      "PTCL.nos..249" "AITL..250"     "AITL..257"     "AITL..110"     "AITL..260"    
[122] "AITL..60"      "AITL..77"      "AITL..84"      "AITL..106"     "AITL..74"      "AITL..133"     "AITL..113"     "AITL..127"     "AITL..44"      "AITL..82"      "AITL..197"    
[133] "AITL..223"     "AITL..17"      "AITL..523"     "AITL..530"     "AITL..154"     "AITL..45"      "AITL..505"     "AITL..2"       "AITL..238"     "AITL..11"      "AITL..259"    
[144] "AITL..10"      "AITL..234"     "AITL..435"     "PTCL.nos..239" "AITL..6"       "AITL..438"     "AITL..518"     "AITL..532"     "AITL..256"     "AITL..449"     "AITL..129"    
[155] "AITL..9"       "AITL..450"     "AITL..534"     "AITL..66"      "AITL..255"     "AITL..207"     "AITL..8"       "AITL..1"       "AITL..152"     "AITL..229"     "AITL..248"    
[166] "AITL..235"     "AITL..420"     "AITL..483"     "AITL..157"     "AITL..179"     "AITL..67"      "AITL..70"      "AITL..225"     "AITL..71"      "AITL..58"      "AITL..78"     
[177] "AITL..137"     "AITL..206"     "AITL..459"     "AITL..144"     "AITL..222"     "PTCL.nos..294" "AITL..198"     "AITL..453"     "AITL..210"     "AITL..26"      "AITL..114"    
[188] "PTCL.nos..226" "AITL..51"      "AITL..224"     "AITL..69"      "AITL..123"     "AITL..221"     "AITL..150"     "AITL..43"      "AITL..153"     "AITL..520"     "AITL..159"    
[199] "AITL..79"      "AITL..204"     "AITL..214"     "PTCL.nos..246" "AITL..167"     "AITL..190"     "PTCL.nos..289" "PTCL.nos..287" "PTCL.nos..288" "PTCL.nos..128" "PTCL.nos..136"
[210] "PTCL.nos..91"  "PTCL.nos..86"  "PTCL.nos..196" "PTCL.nos..212" "PTCL.nos..13"  "PTCL.nos..96"  "PTCL.nos..95"  "PTCL.nos..208" "PTCL.nos..290" "PTCL.nos..115" "PTCL.nos..219"
[221] "AITL..484"     "AITL..7"       "AITL..457"     "AITL..454"     "PTCL.nos..200" "PTCL.nos..215" "PTCL.nos..252" "PTCL.nos..166" "PTCL.nos..218" "PTCL.nos..291" "PTCL.nos..292"
[232] "PTCL.nos..467" "PTCL.nos..482" "AITL..502"     "AITL..515"     "AITL..406"     "PTCL.nos..162" "PTCL.nos..465" "PTCL.nos..203" "PTCL.nos..63"  "PTCL.nos..240" "PTCL.nos..33" 
[243] "AITL..510"     "AITL..513"     "AITL..147"     "AITL..426"     "PTCL.nos..527" "PTCL.nos..414" "PTCL.nos..415" "AITL..14"      "AITL..456"     "AITL..205"     "AITL..55"     
[254] "AITL..64"      "AITL..199"     "AITL..65"      "PTCL.nos..3"   "AITL..121"     "AITL..517"     "PTCL.nos..174" "PTCL.nos..524" "PTCL.nos..243" "PTCL.nos..242" "PTCL.nos..244"
[265] "AITL..392"     "AITL..466"     "AITL..4"       "AITL..5"       "PTCL.nos..241" "AITL..417"     "AITL..461"    
cluster.pts.nr$tree_col$labels 
  [1] "AITL..1"       "AITL..10"      "AITL..106"     "AITL..11"      "AITL..110"     "AITL..113"     "AITL..114"     "AITL..12"      "AITL..121"     "AITL..123"     "AITL..127"    
 [12] "AITL..129"     "AITL..133"     "AITL..137"     "AITL..14"      "AITL..144"     "AITL..147"     "AITL..150"     "AITL..152"     "AITL..153"     "AITL..154"     "AITL..157"    
 [23] "AITL..159"     "AITL..163"     "AITL..165"     "AITL..167"     "AITL..17"      "AITL..179"     "AITL..18"      "AITL..187"     "AITL..19"      "AITL..190"     "AITL..191"    
 [34] "AITL..197"     "AITL..198"     "AITL..199"     "AITL..2"       "AITL..204"     "AITL..205"     "AITL..206"     "AITL..207"     "AITL..210"     "AITL..214"     "AITL..221"    
 [45] "AITL..222"     "AITL..223"     "AITL..224"     "AITL..225"     "AITL..229"     "AITL..234"     "AITL..235"     "AITL..238"     "AITL..248"     "AITL..250"     "AITL..255"    
 [56] "AITL..256"     "AITL..257"     "AITL..259"     "AITL..26"      "AITL..260"     "AITL..392"     "AITL..4"       "AITL..406"     "AITL..411"     "AITL..413"     "AITL..417"    
 [67] "AITL..419"     "AITL..420"     "AITL..426"     "AITL..43"      "AITL..435"     "AITL..438"     "AITL..44"      "AITL..449"     "AITL..45"      "AITL..450"     "AITL..453"    
 [78] "AITL..454"     "AITL..456"     "AITL..457"     "AITL..458"     "AITL..459"     "AITL..461"     "AITL..466"     "AITL..472"     "AITL..473"     "AITL..479"     "AITL..481"    
 [89] "AITL..483"     "AITL..484"     "AITL..487"     "AITL..5"       "AITL..502"     "AITL..505"     "AITL..51"      "AITL..510"     "AITL..513"     "AITL..515"     "AITL..517"    
[100] "AITL..518"     "AITL..520"     "AITL..523"     "AITL..530"     "AITL..532"     "AITL..534"     "AITL..536"     "AITL..55"      "AITL..58"      "AITL..6"       "AITL..60"     
[111] "AITL..62"      "AITL..64"      "AITL..65"      "AITL..66"      "AITL..67"      "AITL..69"      "AITL..7"       "AITL..70"      "AITL..71"      "AITL..74"      "AITL..77"     
[122] "AITL..78"      "AITL..79"      "AITL..8"       "AITL..82"      "AITL..84"      "AITL..9"       "PTCL.nos..100" "PTCL.nos..101" "PTCL.nos..102" "PTCL.nos..104" "PTCL.nos..105"
[133] "PTCL.nos..109" "PTCL.nos..112" "PTCL.nos..115" "PTCL.nos..118" "PTCL.nos..119" "PTCL.nos..120" "PTCL.nos..124" "PTCL.nos..126" "PTCL.nos..128" "PTCL.nos..13"  "PTCL.nos..134"
[144] "PTCL.nos..135" "PTCL.nos..136" "PTCL.nos..139" "PTCL.nos..143" "PTCL.nos..15"  "PTCL.nos..151" "PTCL.nos..156" "PTCL.nos..16"  "PTCL.nos..161" "PTCL.nos..162" "PTCL.nos..166"
[155] "PTCL.nos..171" "PTCL.nos..174" "PTCL.nos..178" "PTCL.nos..180" "PTCL.nos..185" "PTCL.nos..186" "PTCL.nos..189" "PTCL.nos..194" "PTCL.nos..195" "PTCL.nos..196" "PTCL.nos..20" 
[166] "PTCL.nos..200" "PTCL.nos..201" "PTCL.nos..203" "PTCL.nos..208" "PTCL.nos..209" "PTCL.nos..211" "PTCL.nos..212" "PTCL.nos..213" "PTCL.nos..215" "PTCL.nos..216" "PTCL.nos..218"
[177] "PTCL.nos..219" "PTCL.nos..22"  "PTCL.nos..226" "PTCL.nos..23"  "PTCL.nos..230" "PTCL.nos..231" "PTCL.nos..232" "PTCL.nos..236" "PTCL.nos..237" "PTCL.nos..239" "PTCL.nos..24" 
[188] "PTCL.nos..240" "PTCL.nos..241" "PTCL.nos..242" "PTCL.nos..243" "PTCL.nos..244" "PTCL.nos..246" "PTCL.nos..247" "PTCL.nos..249" "PTCL.nos..25"  "PTCL.nos..251" "PTCL.nos..252"
[199] "PTCL.nos..258" "PTCL.nos..27"  "PTCL.nos..28"  "PTCL.nos..287" "PTCL.nos..288" "PTCL.nos..289" "PTCL.nos..29"  "PTCL.nos..290" "PTCL.nos..291" "PTCL.nos..292" "PTCL.nos..293"
[210] "PTCL.nos..294" "PTCL.nos..3"   "PTCL.nos..30"  "PTCL.nos..32"  "PTCL.nos..33"  "PTCL.nos..34"  "PTCL.nos..408" "PTCL.nos..409" "PTCL.nos..414" "PTCL.nos..415" "PTCL.nos..416"
[221] "PTCL.nos..424" "PTCL.nos..428" "PTCL.nos..432" "PTCL.nos..434" "PTCL.nos..440" "PTCL.nos..441" "PTCL.nos..444" "PTCL.nos..445" "PTCL.nos..446" "PTCL.nos..448" "PTCL.nos..451"
[232] "PTCL.nos..452" "PTCL.nos..455" "PTCL.nos..46"  "PTCL.nos..460" "PTCL.nos..463" "PTCL.nos..465" "PTCL.nos..467" "PTCL.nos..468" "PTCL.nos..469" "PTCL.nos..47"  "PTCL.nos..470"
[243] "PTCL.nos..471" "PTCL.nos..482" "PTCL.nos..504" "PTCL.nos..506" "PTCL.nos..519" "PTCL.nos..52"  "PTCL.nos..524" "PTCL.nos..527" "PTCL.nos..529" "PTCL.nos..531" "PTCL.nos..535"
[254] "PTCL.nos..61"  "PTCL.nos..63"  "PTCL.nos..68"  "PTCL.nos..72"  "PTCL.nos..80"  "PTCL.nos..86"  "PTCL.nos..87"  "PTCL.nos..89"  "PTCL.nos..90"  "PTCL.nos..91"  "PTCL.nos..92" 
[265] "PTCL.nos..93"  "PTCL.nos..94"  "PTCL.nos..95"  "PTCL.nos..96"  "PTCL.nos..97"  "PTCL.nos..98"  "PTCL.nos..99" 
#pheatmap::pheatmap(test, filename="test.pdf")

LOOCV on AILT, PTCLnos based on 16-gene model

y = t(mat)
cl.orig = c()
for (u in 1:nrow(y)) cl.orig [u] = unlist(strsplit(rownames(y)[u],"\\."))[1]
perm.mother = rownames(y)
perm.son = combn (perm.mother, length(perm.mother)-1)
output <- cbind(perm.mother, NA)
for (i in 1:length(perm.mother)) {
  train <- y [ perm.son[,i], ]
  test <- y [ ! ( rownames(y) %in% perm.son[,i]) , ]
  cl <- cl.orig [which(rownames(y)%in%perm.son[,i])]
  z <- lda(train, cl)
  p <- predict(z,test)$class
  output  [ setdiff(1:271, which( rownames(y) %in% perm.son[,i]) ) , 2  ] = as.character(p)
#  output  [ output[,1] == rownames(test) , 3  ] = z$scaling [1,1]
#  output  [ output[,1] == rownames(test) , 4  ] = z$scaling [2,1]
#  output  [ output[,1] == rownames(test) , 5  ] = z$scaling [3,1]
}
colnames(output) = c("true","LOOCV.predicted")
output = as.data.frame(output)
output$true.class = cl.orig
table(output$true.class, output$LOOCV.predicted  )
      
       AITL PTCL
  AITL  106   21
  PTCL   16  128
confusionMatrix(table(output$true.class, output$LOOCV.predicted  ))
Confusion Matrix and Statistics

      
       AITL PTCL
  AITL  106   21
  PTCL   16  128
                                         
               Accuracy : 0.8635         
                 95% CI : (0.8168, 0.902)
    No Information Rate : 0.5498         
    P-Value [Acc > NIR] : <2e-16         
                                         
                  Kappa : 0.7252         
 Mcnemar's Test P-Value : 0.5108         
                                         
            Sensitivity : 0.8689         
            Specificity : 0.8591         
         Pos Pred Value : 0.8346         
         Neg Pred Value : 0.8889         
             Prevalence : 0.4502         
         Detection Rate : 0.3911         
   Detection Prevalence : 0.4686         
      Balanced Accuracy : 0.8640         
                                         
       'Positive' Class : AITL           
                                         
# Confusion Matrix and Statistics
# 
#       
#        AITL PTCL
#   AITL  106   21
#   PTCL   16  128
#                                          
#                Accuracy : 0.8635         
#                  95% CI : (0.8168, 0.902)
#     No Information Rate : 0.5498         
#     P-Value [Acc > NIR] : <2e-16         
#                                          
#                   Kappa : 0.7252         
#  Mcnemar's Test P-Value : 0.5108         
#                                          
#             Specificity : 0.8591         
#          Pos Pred Value : 0.8346         
#          Neg Pred Value : 0.8889         
#              Prevalence : 0.4502         
#          Detection Rate : 0.3911         
#    Detection Prevalence : 0.4686         
#       Balanced Accuracy : 0.8640         
#                                          
#        'Positive' Class : AITL      

Use ConsensusClusterPlus to extract most significant clusters: analyze sample stratification based on the extracted differentially expressed genes betwee AILT and PTCL-nos and the ALCL ALK-negative 3-gene model.

select_hist<- pts.info.data[pts.info.data$final.molec == "AITL" | pts.info.data$final.molec == "PTCL.nos" | pts.info.data$final.molec == "ALCL.neg",]
# Add three classifier genes for ALCL ALK-neg [Agnelli et al, Blood, 2012]
# Check on array
anaplastic_gene<- c("TNFRSF8","BATF3","TMOD1")
geneannotation2 <- getBM( attributes = c("entrezgene", "external_gene_name"), filters = "external_gene_name", values = anaplastic_gene, mart = ensembl )
anaplastic_gene_ARRAY<- paste0(geneannotation2$entrezgene, "_at")
# Append 16-gene model to 3-gene model
list_genes_all<- c(list_genes, anaplastic_gene_ARRAY)
# Redo consensus cluster analysis
gep<- geneExpr[,select_hist$sample.nameNEW]
mat<- gep[list_genes_all,]
title=tempdir()
d<- data.matrix(mat)
d = sweep(d,1, apply(d,1,median,na.rm=T))
results = ConsensusClusterPlus(d,maxK=8,
                               pFeature=1,
                               title=title,
                               clusterAlg="hc",
                               innerLinkage="ward.D2",
                               finalLinkage="ward.D2",
                               distance="euclidean",
                               seed=123456789)
end fraction

kk<- as.data.frame((results[[5]]$consensusClass)) ##### 4 significant cluster
kk$geo.id<- rownames(kk)
colnames(kk)[1]<- "cluster"
table(kk$cluster)

  1   2   3   4   5 
 87 103  32  63  55 

Plot heatmap AITL, PTCL-NOS, ALCL-neg and the 19-gene model

heat<- merge(t(mat), kk, by.x = 0, by.y="geo.id")
heat2<- merge(heat, pts.info.data, by.x = 1, by.y="sample.nameNEW")
heat2<- heat2[order(heat2$cluster),]
mycol= c("red","white","yellow")
mylabel = heat2[,c("Row.names","cluster","final.molec","TET2","RHOA","IDH2","DNMT3A")]
colnames(mylabel)<- c("sample.names","clusters","Histology","TET2","RHOA","IDH2","DNMT3A")
rownames(mylabel) = mylabel$sample.names
mylabel$sample.names  = NULL
mylabel.nocol = mylabel
mylabel.col = mylabel
mylabel.col[is.na(mylabel.col)]<-0
#head(mylabel.col)
mylabel.col$Histology[mylabel.col$Histology == "AITL"] = "black"; mylabel.col$Histology[mylabel.col$Histology == "PTCL.nos"] = "orange"; mylabel.col$Histology[mylabel.col$Histology == "ALCL.neg"] = "yellow"
for (a in c(3:6)) mylabel.col[,a] = factor(mylabel.col[,a], levels = levels(as.factor(mylabel.col[,a])), labels = mycol )
mycol_plus<- c(brewer.pal(11,"Paired"),brewer.pal(6,"Set2"))
for (a in 1) mylabel.col[,a] = factor(mylabel.col[,a], levels = levels(as.factor(mylabel.col[,a])), labels = mycol_plus[1:5] )
mylabel.nocol$clusters<-as.numeric(as.character(mylabel.nocol$clusters))
mylabel.nocol$clusters<-as.character(paste("cluster",mylabel.nocol$clusters, sep=""))
  
par(mfrow=c(1,1))
par(mar=c(5,5,5,5), xpd=F)
mat3<- t(data.matrix(heat2[,2:20]))
colnames(mat3)<-heat2$Row.names
mat3= mat3[order(rownames(mat3)),]
temp_name = getBM( attributes = c("ensembl_transcript_id", "entrezgene", "external_gene_name"), filters = "entrezgene", values = gsub("_at","",rownames(mat3)), mart = ensembl)[,c(2:3)]
temp_name = temp_name[!duplicated(temp_name[,1]),]
rownames(mat3) = temp_name$external_gene_name
mat3  <- mat3 - rowMeans(mat3)
par(mfrow=c(1,1))
pheatmap(mat3, annotation_col = mylabel.nocol, annotation_colors = list(clusters = c(cluster1= mycol_plus[1], cluster2 = mycol_plus[2], cluster3 = mycol_plus[3], cluster4 = mycol_plus[4], cluster5 = mycol_plus[5]), Histology = c(AITL = "black", PTCL.nos = "orange", ALCL.neg= "yellow"), IDH2 = c(MUT=mycol[1], "NA"=mycol[2],WT=mycol[3]), RHOA = c(MUT=mycol[1],"NA"=mycol[2],WT=mycol[3]), TET2 = c(MUT=mycol[1],"NA"=mycol[2],WT=mycol[3]), DNMT3A = c(MUT=mycol[1],"NA"=mycol[2],WT=mycol[3]) ) ,  border_color= NA, color = colorRampPalette(rev(brewer.pal(n = 5 , name = "RdYlBu")))(100), scale = "row", cluster_cols = FALSE, show_colnames= F, row_annotation =3, cellheight = 20)
#dev.off()
gep<- geneExpr[,select_hist$sample.nameNEW]
mat<- gep[list_genes_all,]
geneannotation1 <- getBM( attributes = c("ensembl_transcript_id", "entrezgene", "external_gene_name"), filters = "entrezgene", values = gsub("_at","",list_genes_all), mart = ensembl)
sort(unique(geneannotation1$external_gene_name))
 [1] "ADRA2A"     "AL441992.1" "ARHGEF10"   "BATF3"      "C3"         "COL4A4"     "DZIP1"      "EFNB2"      "HS3ST3A1"   "ID2"        "NETO2"      "OSMR"       "PRRX1"     
[14] "ROBO1"      "SLC5A3"     "TMOD1"      "TNFRSF8"    "XKR4"       "YAP1"      
setdiff(rownames(mat), paste0(unique(geneannotation1$entrezgene),"_at"))
character(0)
for (ii in 1:nrow(mat)) {
  #if(length (which (paste0(unique(geneannotation1$entrezgene),"_at") == rownames(mat)[ii])) != 0 ) rownames(mat) [ii] = geneannotation1$external_gene_name [ which (paste0(unique(geneannotation1$entrezgene),"_at") == rownames(mat)[ii])]
  rownames(mat) [ii] = unique(geneannotation1$external_gene_name) [ which (paste0(unique(geneannotation1$entrezgene),"_at") == rownames(mat)[ii])]
}
mycol= c("red","white","yellow")
mylabel = select_hist[,c("sample.nameNEW","final.molec","IDH2","RHOA","TET2","DNMT3A")]
rownames(mylabel) = mylabel$sample.nameNEW
mylabel$sample.nameNEW = NULL
mylabel.nocol = mylabel
mylabel.col = mylabel
mylabel.col[is.na(mylabel.col)]<-0
#head(mylabel.col)
mylabel.col$final.molec[mylabel.col$final.molec == "AITL"] = "black"; mylabel.col$final.molec[mylabel.col$final.molec == "PTCL.nos"] = "orange"; mylabel.col$final.molec[mylabel.col$final.molec == "ALCL.neg"] = "yellow"
for (a in 2:5) mylabel.col[,a] = factor(mylabel.col[,a], levels = levels(as.factor(mylabel.col[,a])), labels = mycol )
mat  <- mat - rowMeans(mat)
par(mfrow=c(1,1))

pheatmap(mat, annotation_col = mylabel.nocol, annotation_colors = list(final.molec = c(AITL = "black", PTCL.nos = "orange", ALCL.neg = "yellow"), filename= "x.pdf",
                                  IDH2 = c(MUT=mycol[1],"NA"=mycol[2],WT=mycol[3]),
                                  RHOA = c(MUT=mycol[1],"NA"=mycol[2],WT=mycol[3]),
                                  TET2 = c(MUT=mycol[1],"NA"=mycol[2],WT=mycol[3]),
                                  DNMT3A = c(MUT=mycol[1],"NA"=mycol[2],WT=mycol[3]) ) , show_colnames = F, cellheight = 15, 
         border_color= NA, color = colorRampPalette(rev(brewer.pal(n = 5 , name = "RdYlBu")))(20), scale = "row", clustering_method = "ward.D2",clustering_distance_cols = "euclidean" , silent = F)

### export pts order
cluster.pts.nr$tree_col$labels [cluster.pts.nr$tree_col$order]
  [1] "PTCL.nos..23"  "PTCL.nos..428" "PTCL.nos..448" "PTCL.nos..124" "PTCL.nos..247" "PTCL.nos..463" "PTCL.nos..89"  "PTCL.nos..156" "PTCL.nos..432" "PTCL.nos..216" "PTCL.nos..25" 
 [12] "PTCL.nos..87"  "PTCL.nos..94"  "PTCL.nos..98"  "PTCL.nos..105" "PTCL.nos..93"  "PTCL.nos..195" "AITL..413"     "PTCL.nos..531" "PTCL.nos..143" "PTCL.nos..46"  "PTCL.nos..28" 
 [23] "PTCL.nos..185" "PTCL.nos..416" "PTCL.nos..112" "PTCL.nos..424" "PTCL.nos..134" "PTCL.nos..32"  "PTCL.nos..22"  "PTCL.nos..194" "PTCL.nos..30"  "PTCL.nos..211" "PTCL.nos..52" 
 [34] "PTCL.nos..97"  "PTCL.nos..201" "PTCL.nos..27"  "PTCL.nos..68"  "PTCL.nos..139" "PTCL.nos..72"  "PTCL.nos..120" "PTCL.nos..444" "PTCL.nos..24"  "PTCL.nos..15"  "PTCL.nos..109"
 [45] "PTCL.nos..29"  "PTCL.nos..100" "PTCL.nos..171" "PTCL.nos..104" "PTCL.nos..99"  "PTCL.nos..126" "PTCL.nos..258" "AITL..536"     "PTCL.nos..535" "PTCL.nos..20"  "PTCL.nos..102"
 [56] "PTCL.nos..452" "PTCL.nos..529" "PTCL.nos..90"  "PTCL.nos..230" "PTCL.nos..231" "PTCL.nos..232" "PTCL.nos..236" "PTCL.nos..16"  "PTCL.nos..189" "PTCL.nos..506" "PTCL.nos..519"
 [67] "PTCL.nos..151" "PTCL.nos..186" "AITL..419"     "PTCL.nos..455" "PTCL.nos..47"  "PTCL.nos..213" "PTCL.nos..161" "PTCL.nos..61"  "PTCL.nos..209" "PTCL.nos..119" "PTCL.nos..80" 
 [78] "PTCL.nos..34"  "PTCL.nos..440" "AITL..19"      "AITL..18"      "PTCL.nos..504" "PTCL.nos..118" "PTCL.nos..293" "PTCL.nos..92"  "PTCL.nos..251" "PTCL.nos..101" "PTCL.nos..446"
 [89] "AITL..479"     "PTCL.nos..469" "AITL..411"     "AITL..473"     "AITL..472"     "AITL..481"     "AITL..487"     "PTCL.nos..434" "PTCL.nos..180" "PTCL.nos..135" "PTCL.nos..445"
[100] "PTCL.nos..408" "PTCL.nos..409" "PTCL.nos..460" "PTCL.nos..468" "PTCL.nos..470" "PTCL.nos..471" "PTCL.nos..441" "PTCL.nos..451" "AITL..12"      "PTCL.nos..237" "AITL..165"    
[111] "PTCL.nos..178" "AITL..458"     "AITL..191"     "AITL..163"     "AITL..187"     "AITL..62"      "PTCL.nos..249" "AITL..250"     "AITL..257"     "AITL..110"     "AITL..260"    
[122] "AITL..60"      "AITL..77"      "AITL..84"      "AITL..106"     "AITL..74"      "AITL..133"     "AITL..113"     "AITL..127"     "AITL..44"      "AITL..82"      "AITL..197"    
[133] "AITL..223"     "AITL..17"      "AITL..523"     "AITL..530"     "AITL..154"     "AITL..45"      "AITL..505"     "AITL..2"       "AITL..238"     "AITL..11"      "AITL..259"    
[144] "AITL..10"      "AITL..234"     "AITL..435"     "PTCL.nos..239" "AITL..6"       "AITL..438"     "AITL..518"     "AITL..532"     "AITL..256"     "AITL..449"     "AITL..129"    
[155] "AITL..9"       "AITL..450"     "AITL..534"     "AITL..66"      "AITL..255"     "AITL..207"     "AITL..8"       "AITL..1"       "AITL..152"     "AITL..229"     "AITL..248"    
[166] "AITL..235"     "AITL..420"     "AITL..483"     "AITL..157"     "AITL..179"     "AITL..67"      "AITL..70"      "AITL..225"     "AITL..71"      "AITL..58"      "AITL..78"     
[177] "AITL..137"     "AITL..206"     "AITL..459"     "AITL..144"     "AITL..222"     "PTCL.nos..294" "AITL..198"     "AITL..453"     "AITL..210"     "AITL..26"      "AITL..114"    
[188] "PTCL.nos..226" "AITL..51"      "AITL..224"     "AITL..69"      "AITL..123"     "AITL..221"     "AITL..150"     "AITL..43"      "AITL..153"     "AITL..520"     "AITL..159"    
[199] "AITL..79"      "AITL..204"     "AITL..214"     "PTCL.nos..246" "AITL..167"     "AITL..190"     "PTCL.nos..289" "PTCL.nos..287" "PTCL.nos..288" "PTCL.nos..128" "PTCL.nos..136"
[210] "PTCL.nos..91"  "PTCL.nos..86"  "PTCL.nos..196" "PTCL.nos..212" "PTCL.nos..13"  "PTCL.nos..96"  "PTCL.nos..95"  "PTCL.nos..208" "PTCL.nos..290" "PTCL.nos..115" "PTCL.nos..219"
[221] "AITL..484"     "AITL..7"       "AITL..457"     "AITL..454"     "PTCL.nos..200" "PTCL.nos..215" "PTCL.nos..252" "PTCL.nos..166" "PTCL.nos..218" "PTCL.nos..291" "PTCL.nos..292"
[232] "PTCL.nos..467" "PTCL.nos..482" "AITL..502"     "AITL..515"     "AITL..406"     "PTCL.nos..162" "PTCL.nos..465" "PTCL.nos..203" "PTCL.nos..63"  "PTCL.nos..240" "PTCL.nos..33" 
[243] "AITL..510"     "AITL..513"     "AITL..147"     "AITL..426"     "PTCL.nos..527" "PTCL.nos..414" "PTCL.nos..415" "AITL..14"      "AITL..456"     "AITL..205"     "AITL..55"     
[254] "AITL..64"      "AITL..199"     "AITL..65"      "PTCL.nos..3"   "AITL..121"     "AITL..517"     "PTCL.nos..174" "PTCL.nos..524" "PTCL.nos..243" "PTCL.nos..242" "PTCL.nos..244"
[265] "AITL..392"     "AITL..466"     "AITL..4"       "AITL..5"       "PTCL.nos..241" "AITL..417"     "AITL..461"    

Cibersort algorithm to characterize the tumour and microenviroment composition of each cluster

##### cibersort and origical molecular histologies
load("./Rmd.files/cibersort.all.Rdata")
ciber_all<-as.data.frame.matrix(t(cibersort.percentages))
ciber_all$sample.nameNEW <- rownames(ciber_all)
colnames(kk)[2]<-"sample.nameNEW"
require(plyr)
final <-join(ciber_all, kk, by = "sample.nameNEW",  type="left")
final2<-merge(pts.info.data[,c(1,6,14:17)], final, by="sample.nameNEW")
final3<- subset(final2, final.molec %in% c("AITL","ALCL.neg","ALCL.pos","ATLL","NKT","PTCL.nos"))
final3<- final3[order(final3$final.molec),]
library(RColorBrewer)
n <- 22
qual_col_pals = brewer.pal.info[brewer.pal.info$category == 'qual',]
col_vector = unlist(mapply(brewer.pal, qual_col_pals$maxcolors, rownames(qual_col_pals)))
par(mar=c(2,5,7,10), xpd=TRUE)
x<- barplot(t(final3[7:28]), names.arg = rep("", length(final3$final.molec)), cex.names = 0.7, col=col_vector, border=NA,
            space=rep(0, nrow(final3)))
legend("topright",legend=colnames(final3)[7:28], col=col_vector, pch=c(15), inset=c(-0.11,0), pt.cex= 1,
cex = 1, bty = "n",  x.intersp = 0.7)
names_hist<- unique(final3$final.molec)
col_hist<- c("orange","yellow","dodgerblue2","brown2","darkorchid1","black")
num<- as.numeric(table(final3$final.molec))
for(i in (1:length(num)))
{
  segments(x[sum(num[1:i])+1-num[i]], 1.05,x[sum(num[1:i])],1.05,lwd=4, col=col_hist[i])
  text(x[(sum(num[1:i])-num[i] +1+ sum(num[1:i]))/2], 1.1, names_hist[i], cex=1.2, srt=0)
}



##### plot cibersort profile of patients stratified according to histology and clusters

for(i in (1:nrow(final3)))
{
final3$cluster[i][is.na(final3$cluster[i])]<- final3$final.molec[i]
}

final3$cluster <- factor(final3$cluster, levels = c( "1","2","4","NKT","3","5","ALCL.pos", "ATLL"))

final3<- final3[order(final3$cluster),]

#pdf("barplot_cibersort.pdf", width = 20, height = 7)
par(mar=c(2,5,7,10), xpd=TRUE)
x<- barplot(t(final3[7:28]), names.arg = rep("", length(final3$final.molec)), cex.names = 0.7, col=col_vector, border=NA,
            space=rep(0, nrow(final3)))
legend("topright",legend=colnames(final3)[7:28], col=col_vector, pch=c(15), inset=c(-0.11,0), pt.cex= 1,
cex = 1, bty = "n",  x.intersp = 0.7)

mycol_plus<- c(brewer.pal(11,"Paired"),brewer.pal(6,"Dark2"))
names_hist<- c("C-1","C-2", "C-4","NKT","C-3","C-5","ALCL.pos","ATLL")
col_hist<- c(mycol_plus[1],mycol_plus[2],mycol_plus[4],"darkorchid1",mycol_plus[3],mycol_plus[5],"dodgerblue2","brown2","")
num<- as.numeric(table(final3$cluster))
  par(new=TRUE)
for(i in (1:(length(num))))
{
  segments(x[sum(num[1:i])+1-num[i]], 1.05,x[sum(num[1:i])],1.05,lwd=4, col=col_hist[i])
  text(x[(sum(num[1:i])-num[i] +1+ sum(num[1:i]))/2], 1.1, names_hist[i], cex=1.2, srt=0)

}
# dev.off()

Boxplot comparing the contribution of each cibersort signature between all extracted clusters

par(mfrow=c(1,2))
par(mar=c(3,3,3,3), xpd=F)
for(i in (7:27))
{
  #pdf(sprintf("%s_cibersort_ptcl.pdf",i), height=8, width=10)
  k<- as.numeric(final2[,i])
  table_wilk<- pairwise.wilcox.test(k,final2$cluster,p.adjust.methods = "bonferroni" )$p.value
  df_wilk <- data.frame(expand.grid(dimnames(table_wilk)),array(table_wilk))
  df_wilk2<-na.omit(df_wilk)
  df_wilk2_sig<- df_wilk2[df_wilk2$array.table_wilk.<0.05,]
  df_wilk2_sig$Var1<-as.numeric(as.character(df_wilk2_sig$Var1))
  df_wilk2_sig$Var2<-as.numeric(as.character(df_wilk2_sig$Var2))
  if(nrow(df_wilk2_sig)>0)
  {
  boxplot(k~final2$cluster, ylim=c(0,(max(k)+0.2)), main=colnames(final2)[i], cex.main=2, col=mycol_plus, las=2)
  for(j in (1:nrow(df_wilk2_sig)))
  {
    segments(df_wilk2_sig$Var1[j], max(k)-0.01+j/30, df_wilk2_sig$Var2[j],max(k)-0.01+j/30)
    p<-df_wilk2_sig$array.table_wilk.[j]
    if(p<0.00001){p2 = "<0.00001"}else{
    p2<-as.numeric(formatC(p,digits=6,format="f"))}
    pval <- paste("p =",p2,sep=" ")
    text((df_wilk2_sig$Var1[j]+ df_wilk2_sig$Var2[j])/1.9,  max(k) +j/30, pval, cex=0.8)
  }
    }
  #dev.off()   
   }

R tmod package analysis

# for convenience: reimport annotated matrix
final<- read.delim("./Rmd.files/aitl_nos_alcl_clsutering.txt",sep="\t",header = T,stringsAsFactors = F)
final2<- final[,c("Row.names","hist","cluster")]
mat<- read.delim("./Rmd.files/ensembl_annotated_matrix.txt", sep="\t", stringsAsFactors = F)
design <- model.matrix(~ 0+factor(final2$cluster)) ##### create matrix
colnames(design)<-paste0("Cluster_",c(1:5))
contrast.matrix <- makeContrasts(Cluster_2-Cluster_1, Cluster_3-Cluster_1,Cluster_4-Cluster_1, Cluster_5-Cluster_1,
                                 Cluster_3-Cluster_2, Cluster_4-Cluster_2, Cluster_5-Cluster_2,
                                 Cluster_4-Cluster_3, Cluster_5-Cluster_3,
                                 Cluster_4-Cluster_5,
                                 Cluster_2-(Cluster_1 + Cluster_3 + Cluster_4 + Cluster_5)/4,
                                 Cluster_3-(Cluster_1 + Cluster_2 + Cluster_4 + Cluster_5)/4,
                                 Cluster_4-(Cluster_1 + Cluster_2 + Cluster_3 + Cluster_5)/4,
                                 Cluster_1-(Cluster_2 + Cluster_3 + Cluster_4 + Cluster_5)/4,
                                 Cluster_5-(Cluster_2 + Cluster_3 + Cluster_4 + Cluster_1)/4,
                                 levels=design)
fit1 <- lmFit(mat, design)
fit2 <- contrasts.fit(fit1, contrast.matrix)
fit <- eBayes(fit2)
geneExpr = adj.data
geneExpr2<- geneExpr[,colnames(geneExpr) %in% final2$Row.names ]
geneExpr2<- geneExpr2[,final2$Row.names]
ensembl = useMart( "ensembl", dataset = "hsapiens_gene_ensembl" )
hgnc <- getBM(attributes=c('entrezgene','hgnc_symbol','hgnc_id'),filters = 'entrezgene', values = gsub("_at","",rownames(geneExpr2)),mart = ensembl)

Batch submitting query [========>-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------]   5% eta:  8s
Batch submitting query [=============>------------------------------------------------------------------------------------------------------------------------------------------------------------------------------]   7% eta:  9s
Batch submitting query [=================>--------------------------------------------------------------------------------------------------------------------------------------------------------------------------]  10% eta:  9s
Batch submitting query [======================>---------------------------------------------------------------------------------------------------------------------------------------------------------------------]  12% eta: 13s
Batch submitting query [===========================>----------------------------------------------------------------------------------------------------------------------------------------------------------------]  15% eta: 12s
Batch submitting query [===============================>------------------------------------------------------------------------------------------------------------------------------------------------------------]  17% eta: 11s
Batch submitting query [====================================>-------------------------------------------------------------------------------------------------------------------------------------------------------]  20% eta: 11s
Batch submitting query [========================================>---------------------------------------------------------------------------------------------------------------------------------------------------]  22% eta: 11s
Batch submitting query [=============================================>----------------------------------------------------------------------------------------------------------------------------------------------]  24% eta: 10s
Batch submitting query [=================================================>------------------------------------------------------------------------------------------------------------------------------------------]  27% eta: 10s
Batch submitting query [======================================================>-------------------------------------------------------------------------------------------------------------------------------------]  29% eta: 10s
Batch submitting query [===========================================================>--------------------------------------------------------------------------------------------------------------------------------]  32% eta: 10s
Batch submitting query [===============================================================>----------------------------------------------------------------------------------------------------------------------------]  34% eta: 10s
Batch submitting query [====================================================================>-----------------------------------------------------------------------------------------------------------------------]  37% eta:  9s
Batch submitting query [========================================================================>-------------------------------------------------------------------------------------------------------------------]  39% eta:  9s
Batch submitting query [=============================================================================>--------------------------------------------------------------------------------------------------------------]  41% eta:  8s
Batch submitting query [==================================================================================>---------------------------------------------------------------------------------------------------------]  44% eta:  8s
Batch submitting query [======================================================================================>-----------------------------------------------------------------------------------------------------]  46% eta:  8s
Batch submitting query [===========================================================================================>------------------------------------------------------------------------------------------------]  49% eta:  7s
Batch submitting query [===============================================================================================>--------------------------------------------------------------------------------------------]  51% eta:  7s
Batch submitting query [====================================================================================================>---------------------------------------------------------------------------------------]  54% eta:  7s
Batch submitting query [========================================================================================================>-----------------------------------------------------------------------------------]  56% eta:  7s
Batch submitting query [=============================================================================================================>------------------------------------------------------------------------------]  59% eta:  6s
Batch submitting query [==================================================================================================================>-------------------------------------------------------------------------]  61% eta:  6s
Batch submitting query [======================================================================================================================>---------------------------------------------------------------------]  63% eta:  6s
Batch submitting query [===========================================================================================================================>----------------------------------------------------------------]  66% eta:  6s
Batch submitting query [===============================================================================================================================>------------------------------------------------------------]  68% eta:  5s
Batch submitting query [====================================================================================================================================>-------------------------------------------------------]  71% eta:  5s
Batch submitting query [=========================================================================================================================================>--------------------------------------------------]  73% eta:  5s
Batch submitting query [=============================================================================================================================================>----------------------------------------------]  76% eta:  4s
Batch submitting query [==================================================================================================================================================>-----------------------------------------]  78% eta:  4s
Batch submitting query [======================================================================================================================================================>-------------------------------------]  80% eta:  3s
Batch submitting query [===========================================================================================================================================================>--------------------------------]  83% eta:  3s
Batch submitting query [===============================================================================================================================================================>----------------------------]  85% eta:  3s
Batch submitting query [====================================================================================================================================================================>-----------------------]  88% eta:  2s
Batch submitting query [=========================================================================================================================================================================>------------------]  90% eta:  2s
Batch submitting query [=============================================================================================================================================================================>--------------]  93% eta:  1s
Batch submitting query [==================================================================================================================================================================================>---------]  95% eta:  1s
Batch submitting query [======================================================================================================================================================================================>-----]  98% eta:  0s
Batch submitting query [============================================================================================================================================================================================] 100% eta:  0s
                                                                                                                                                                                                                                   
geneExpr3<- as.data.frame.matrix(geneExpr2[which(rownames(geneExpr2) %in% paste0(hgnc$entrezgene,"_at")),])
levels_design<- c("Cluster_2-Cluster_1","Cluster_3-Cluster_1","Cluster_4-Cluster_1","Cluster_5-Cluster_1",
                 "Cluster_3-Cluster_2","Cluster_4-Cluster_2","Cluster_5-Cluster_2","Cluster_4-Cluster_3",
                 "Cluster_5-Cluster_3","Cluster_4-Cluster_5",
                 "Cluster_2-(Cluster_1 + Cluster_3 + Cluster_4 + Cluster_5)/4",
                 "Cluster_3-(Cluster_1 + Cluster_2 + Cluster_4 + Cluster_5)/4",
                 "Cluster_4-(Cluster_1 + Cluster_2 + Cluster_3 + Cluster_5)/4",
                 "Cluster_1-(Cluster_2 + Cluster_3 + Cluster_4 + Cluster_5)/4",
                 "Cluster_5-(Cluster_2 + Cluster_3 + Cluster_4 + Cluster_1)/4")
df_diff_all=NULL
for(i in (1:length(levels_design)))
{
tt <- topTable(fit, coef=i, number=Inf, genelist=rownames(geneExpr3))
tt$ID<- rownames(tt)
colnames(tt)[1]<-"GENE_SYMBOL"
head(tt, 10)
fg <- tt$GENE_SYMBOL[tt$adj.P.Val < 0.001 & abs( tt$logFC ) > 2]
length(fg)
df_diff<- cbind(fg, rep(levels_design[i], length(fg)))
df_diff_all<-rbind(df_diff_all, df_diff)
#plot(tt$logFC, -log10(tt$adj.P.Val))
}
df_diff_all<- as.data.frame.matrix(df_diff_all)
annotation_col<- final2
colnames(annotation_col)<-c("sampleID","Hist","cluster")
A <- function(x) (as.factor(as.character(x))) ##### lapply function for all columns to generate the relative contribution
annotation_col[,1:ncol(annotation_col)] = apply(annotation_col[,1:ncol(annotation_col)], 2, function(x) as.factor(as.character(x)))
annotation_col<- as.data.frame(annotation_col[,-1])
mycol_plus<- c(brewer.pal(11,"Paired"),brewer.pal(6,"Dark2"))
ann_colors = list(Hist=c( "AITL"="black","ALCL"="yellow","PTCL"="orange"),
                  cluster=c("1" = mycol_plus[1],"2" = mycol_plus[2],"3" = mycol_plus[3],"4" = mycol_plus[4],"5" =mycol_plus[5])
                  )
edata3<- mat[rownames(mat) %in% unique(df_diff_all$fg),]
pheatmap(as.matrix( as.matrix(edata3)), annotation_col=annotation_col, annotation_colors = ann_colors, border_color="NA", scale = "row", cluster_cols = FALSE, show_colnames= F, show_rownames = FALSE)

levels_design<- c("Cluster_2-Cluster_1","Cluster_3-Cluster_1","Cluster_4-Cluster_1","Cluster_5-Cluster_1",
                 "Cluster_3-Cluster_2","Cluster_4-Cluster_2","Cluster_5-Cluster_2","Cluster_4-Cluster_3",
                 "Cluster_5-Cluster_3","Cluster_4-Cluster_5",
                 "Cluster_2-(Cluster_1 + Cluster_3 + Cluster_4 + Cluster_5)/4",
                 "Cluster_3-(Cluster_1 + Cluster_2 + Cluster_4 + Cluster_5)/4",
                 "Cluster_4-(Cluster_1 + Cluster_2 + Cluster_3 + Cluster_5)/4",
                 "Cluster_1-(Cluster_2 + Cluster_3 + Cluster_4 + Cluster_5)/4",
                 "Cluster_5-(Cluster_2 + Cluster_3 + Cluster_4 + Cluster_1)/4")
df_diff_all=NULL
for(i in (1:length(levels_design)))
{
tt <- topTable(fit, coef=i, number=Inf, genelist=rownames(geneExpr3))
tt$ID<- rownames(tt)
colnames(tt)[1]<-"GENE_SYMBOL"
head(tt, 10)
fg <- tt$GENE_SYMBOL[tt$adj.P.Val < 0.001 & abs( tt$logFC ) > 2]
length(fg)
df_diff<- cbind(fg, rep(levels_design[i], length(fg)))
df_diff_all<-rbind(df_diff_all, df_diff)
#plot(tt$logFC, -log10(tt$adj.P.Val))
}
df_diff_all<- as.data.frame.matrix(df_diff_all)
table(df_diff_all$V2)

Cluster_1-(Cluster_2 + Cluster_3 + Cluster_4 + Cluster_5)/4 Cluster_3-(Cluster_1 + Cluster_2 + Cluster_4 + Cluster_5)/4                                         Cluster_3-Cluster_1 
                                                         18                                                          84                                                         203 
                                        Cluster_3-Cluster_2                                         Cluster_4-Cluster_1                                         Cluster_4-Cluster_2 
                                                        121                                                          15                                                           2 
                                        Cluster_4-Cluster_3                                         Cluster_4-Cluster_5 Cluster_5-(Cluster_2 + Cluster_3 + Cluster_4 + Cluster_1)/4 
                                                         78                                                           8                                                           5 
                                        Cluster_5-Cluster_1                                         Cluster_5-Cluster_2                                         Cluster_5-Cluster_3 
                                                         41                                                          19                                                          74 
annotation_col<- final2
colnames(annotation_col)<-c("sampleID","Hist","cluster")
A <- function(x) (as.factor(as.character(x))) ##### lapply function for all columns to generate the relative contribution
annotation_col[,1:ncol(annotation_col)] = apply(annotation_col[,1:ncol(annotation_col)], 2, function(x) as.factor(as.character(x)))
annotation_col<- as.data.frame(annotation_col[,-1])
mycol_plus<- c(brewer.pal(11,"Paired"),brewer.pal(6,"Dark2"))
ann_colors = list(Hist=c( "AITL"="black","ALCL"="yellow","PTCL"="orange"),
                  cluster=c("1" = mycol_plus[1],"2" = mycol_plus[2],"3" = mycol_plus[3],"4" = mycol_plus[4],"5" =mycol_plus[5])
                  )
edata3<- mat[rownames(mat) %in% unique(df_diff_all$fg),]
pheatmap(as.matrix( as.matrix(edata3)), annotation_col=annotation_col, annotation_colors = ann_colors, border_color="NA",
         scale = "row", cluster_cols = FALSE, show_colnames= F, show_rownames = FALSE)

############ table of genes
df_diff_all_tab=NULL
for(i in (1:length(levels_design)))
{
  tt <- topTable(fit, coef=i, number=Inf, genelist=rownames(geneExpr3))
  tt$ID<- rownames(tt)
  colnames(tt)[1]<-"GENE_SYMBOL"
  head(tt,10)
  fg <- tt[tt$adj.P.Val < 0.001 & abs( tt$logFC ) > 2,]
  if(nrow(fg)>0){
    fg$design<- levels_design[i]
  df_diff_all_tab<-rbind.data.frame(df_diff_all_tab, fg)
  #plot(tt$logFC"," -log10(tt$adj.P.Val))
  }
  }
nrow(df_diff_all_tab) #### number of genes differentially expressed between C-1, C-2, C-3, C-4, C-5
[1] 668
##### list gene from Iqbal et al. blood 2014
iqbal<- unique(c("EFNB2","ROBO1","S1PR3","ANK2","LPAR1","SNAP91","SOX8","LPAR1","RAMP3","S1PR3","ROBO1","EFNB2","TUBB2B","SOX8",
                 "SOX8","ARHGEF10","DMRT1",  "SLC19A21","STK3","PERP","TNFRSF8","TMOD1","BATF3","CDC14B","PERP","WDFEY3",
                 "TMOD1","ATP6V0D1","AXL","CD59","CHI3L1","CLTC","COL6A1","CREG1","CTSB","CTSC","NR1","H3","PDXK","PITPNA",
                 "PLSCR1","PRDX3","CTSS","CYBB","FABP3","FPR1","FTL","GUCA2A","HCK","IFI30","IL13RA1","JAK2","LILRB1",
                 "PRKG1PSAP","SLC7A7","SOD2","TCN2","THY1","TYR","UBE2L6","WARS","AXL","FTL","SIRPA","STAT1","CSF2","IFNG",
                 "SEPT6","GATA3","CD28","STAT1","AXL","CD28","CD40","CD59","CSF2","FTL","IFNG","LILRB1","SIRPA","TBX21",
                 "MSH6","EGR1","CAT","EGR1","CAT"))
intersect(iqbal, unique(df_diff_all_tab$GENE_SYMBOL))
 [1] "ROBO1"    "LPAR1"    "SOX8"     "TUBB2B"   "TNFRSF8"  "TMOD1"    "BATF3"    "ATP6V0D1" "CHI3L1"   "CREG1"    "CTSB"     "CTSC"     "FTL"      "HCK"     

pairwise for pathway using tmod (https://cran.r-project.org/web/packages/tmod/vignettes/tmod.pdf)

fit1 <- lmFit(mat, design)
fit2 <- contrasts.fit(fit1, contrast.matrix)
fit <- eBayes(fit2)
res.l <- tmodLimmaTest(fit, rownames(mat))
length(res.l)
[1] 15
names(res.l)
 [1] "Cluster_2 - Cluster_1"                                         "Cluster_3 - Cluster_1"                                         "Cluster_4 - Cluster_1"                                        
 [4] "Cluster_5 - Cluster_1"                                         "Cluster_3 - Cluster_2"                                         "Cluster_4 - Cluster_2"                                        
 [7] "Cluster_5 - Cluster_2"                                         "Cluster_4 - Cluster_3"                                         "Cluster_5 - Cluster_3"                                        
[10] "Cluster_4 - Cluster_5"                                         "Cluster_2 - (Cluster_1 + Cluster_3 + Cluster_4 + Cluster_5)/4" "Cluster_3 - (Cluster_1 + Cluster_2 + Cluster_4 + Cluster_5)/4"
[13] "Cluster_4 - (Cluster_1 + Cluster_2 + Cluster_3 + Cluster_5)/4" "Cluster_1 - (Cluster_2 + Cluster_3 + Cluster_4 + Cluster_5)/4" "Cluster_5 - (Cluster_2 + Cluster_3 + Cluster_4 + Cluster_1)/4"
pie <- tmodLimmaDecideTests(fit, genes=rownames(mat))
par(mfrow=c(1,1))
res.l2<- lapply(res.l, function(x) {x[x$adj.P.Val<10e-8,]})
tmodPanelPlot(res.l2, pie=pie, text.cex=0.6) ##### zero = grey, blue down in the first factor and red up in the first

res.l2<- lapply(res.l, function(x) {x[x$adj.P.Val>10e-8 & x$adj.P.Val<10e-5,]})
tmodPanelPlot(res.l2, pie=pie, text.cex=0.6) ##### zero = grey, blue down in the first factor and red up in the first

LS0tCnRpdGxlOiAiUFRDTCBHZW5lIEV4cHJlc3Npb24gYmFzZWQgY2xhc3NpZmljYXRpb24iCmF1dGhvcjoKLSBhZmZpbGlhdGlvbjogVW5pdmVyc2l0eSBvZiBNaWxhbgogIG5hbWU6IEx1Y2EgQWduZWxsaQotIGFmZmlsaWF0aW9uOiBXZWxsY29tZSBUcnVzdCBTYW5nZXIgSW5zdGl0dXRlLCBVSyBhbmQgVW5pdmVyc2l0eSBvZiBNaWxhbiwgSVQKICBuYW1lOiBGcmFuY2VzY28gTWF1cmEKZGF0ZTogIjIwMTgvMDcvMTgiCm91dHB1dDoKICBwZGZfZG9jdW1lbnQ6IAogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDIKICBodG1sX2RvY3VtZW50OiAKICAgIGFsd2F5c19hbGxvd19odG1sOiB5ZXMKICBodG1sX25vdGVib29rOiBkZWZhdWx0CnN1YnRpdGxlOiBJbnRlZ3JhdGlvbiBvZiBUcmFuc2NyaXB0aW9uYWwgYW5kIE11dGF0aW9uYWwgRGF0YSBJbXByb3ZlcyB0aGUgU3RyYXRpZmljYXRpb24KICBvZiBQZXJpcGhlcmFsIFQtQ2VsbCBMeW1waG9tYSBzZXJpZXMKLS0tCkJ1aWx0IHdpdGggUiB2ZXJzaW9uOiAgCmByIGdldFJ2ZXJzaW9uKClgCgotLS0KQnVpbHQgd2l0aCBSIHZlcnNpb246ICAKYHIgZ2V0UnZlcnNpb24oKWAKCiMgTGlicmFyaWVzCgpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKCmBgYHtyIHNldHVwLCBpbmNsdWRlPVRSVUUsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoYWZmeSkKbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShwbG90M0QpCmxpYnJhcnkoZ3Bsb3RzKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KEFubm90YXRpb25EYmkpCmxpYnJhcnkobGltbWEpCmxpYnJhcnkobGF0dGljZSkKbGlicmFyeShvcmcuSHMuZWcuZGIpCmxpYnJhcnkoTUFTUykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoQW5ub3RhdGlvbkRiaSkKbGlicmFyeShyZ2x3aWRnZXQpCiMjI2xpYnJhcnkoaGd1MTMzcGx1czJoc2VudHJlemdjZGYpCmxpYnJhcnkoVmVubkRpYWdyYW0pCmxpYnJhcnkob3JnLkhzLmVnLmRiKQpsaWJyYXJ5KEdlbm9taWNSYW5nZXMpCmxpYnJhcnkoR2Vub21pY0ZlYXR1cmVzKQpsaWJyYXJ5KHJ0cmFja2xheWVyKQpsaWJyYXJ5KGJpb21hUnQpCmxpYnJhcnkoZ2xtbmV0KQpsaWJyYXJ5KHN1cnZpdmFsKQpsaWJyYXJ5KEhtaXNjKQpsaWJyYXJ5KENvbnNlbnN1c0NsdXN0ZXJQbHVzKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoaGVhdG1hcC5wbHVzKQpsaWJyYXJ5KHJnbCkKbGlicmFyeShjYXJldCkKbGlicmFyeShlMTA3MSkKbGlicmFyeSh0bW9kKQoKc2V0MSA9IGMoYnJld2VyLnBhbCg5LCJTZXQxIiksIGJyZXdlci5wYWwoOCwgIkRhcmsyIikpCgp2aW9saW5KaXR0ZXIgPC0gZnVuY3Rpb24oeCwgbWFnbml0dWRlPTEpewogIGQgPC0gZGVuc2l0eSh4KQogIGRhdGEuZnJhbWUoeD14LCB5PXJ1bmlmKGxlbmd0aCh4KSwtbWFnbml0dWRlLzIsIG1hZ25pdHVkZS8yKSAqIGFwcHJveGZ1bihkJHgsIGQkeSkoeCkpCn0KCnJvdGF0ZWRMYWJlbCA8LSBmdW5jdGlvbih4MCA9IHNlcV9hbG9uZyhsYWJlbHMpLCB5MCA9IHJlcChwYXIoInVzciIpWzNdLCBsZW5ndGgobGFiZWxzKSksIGxhYmVscywgcG9zID0gMSwgY2V4PTEsIHNydD00NSwgLi4uKSB7CiAgdyA8LSBzdHJ3aWR0aChsYWJlbHMsIHVuaXRzPSJ1c2VyIiwgY2V4PWNleCkKICBoIDwtIHN0cmhlaWdodChsYWJlbHMsIHVuaXRzPSJ1c2VyIixjZXg9Y2V4KQogIHUgPC0gcGFyKCd1c3InKQogIHAgPC0gcGFyKCdwbHQnKQogIGYgPC0gcGFyKCJmaW4iKQogIHhwZCA8LSBwYXIoInhwZCIpCiAgcGFyKHhwZD1OQSkKICB0ZXh0KHg9eDAgKyBpZmVsc2UocG9zPT0xLCAtMSwxKSAqIHcvMipjb3Moc3J0LzM2MCoyKmJhc2U6OnBpKSwgeSA9IHkwICsgaWZlbHNlKHBvcz09MSwgLTEsMSkgKiB3LzIgKnNpbihzcnQvMzYwKjIqYmFzZTo6cGkpICogKHVbNF0tdVszXSkvKHVbMl0tdVsxXSkgLyAocFs0XS1wWzNdKSAqIChwWzJdLXBbMV0pKiBmWzFdL2ZbMl0gLCBsYWJlbHMsIGxhcz0yLCBjZXg9Y2V4LCBwb3M9cG9zLCBhZGo9MSwgc3J0PXNydCwuLi4pCiAgcGFyKHhwZD14cGQpCn0KCgphdmVmYyA9IGZ1bmN0aW9uICh5LCBsb2c9VFJVRSwgcmVwbGFjZT0gRkFMU0UpIHsKICAgICBpZiAobG9nKSB5ID0gMl55CiAgIGlmIChyZXBsYWNlKSB5ID0geSArICgxLW1pbih5KSkKICAgbSA9IGFwcGx5KHksMSxtZWFuKQogICAgIHkubiA9IHkvbSAgCiAgICAgeS5uMiA9IHkubgogICAgIHkubjIgW3kubjIgPCAxXSA9IDEvICh5Lm4yIFt5Lm4yIDwgMV0pCiAgICAgYXZlLmZjID0gYXBwbHkgKHkubjIsIDEsIG1lYW4pCiAgICAgcmV0dXJuKGF2ZS5mYykKICAgICB9CgpgYGAKCiMjIEVuc2VtYmwgTGlicmFyeQoKRm9yICBnZW5lIGNvbnZlcnRpb24gZnJvbSBhcnJheSB0byBIVUdPCgpgYGB7cn0KZW5zZW1ibCA9IHVzZU1hcnQoICJlbnNlbWJsIiwgZGF0YXNldCA9ICJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiICkKYGBgCgojIyBHZW5lIEV4cHJlc3Npb24gRGF0YQoKVXBsb2FkIG9yIGdlbmVyYXRlIEdFUCBub3JtYWxpemVkIG1hdHJpeAoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CiMjIyBjaG9pY2UgMTogaW1wb3J0IHByb2Nlc3NlZCBtYXRyaXgKIyBkYXRhLmRpcj0iLi9SbWQuZmlsZXMvIgpkYXRhLmRpciA9ICcvVXNlcnMvZW1hZ2VuZS9Ecm9wYm94L2NvZGVzL2dpdGh1Yi9QVENMLycKc2V0d2QoZGF0YS5kaXIpCmxvYWQgKGZpbGUucGF0aChkYXRhLmRpciwiL1JtZC5maWxlcy81NDFfUFRDTF9iYXRjaF9hZGp1c3RlZF9nZW8uaWQuUmRhdGEiKSkKCmdlbmVFeHByID0gYWRqLmRhdGEKIyBpbXBvcnQgYmF0Y2ggYW5kIHJlLW9yZGVyIGFjY29yZGluZ2x5CmxvYWQoZmlsZS5wYXRoKGRhdGEuZGlyLCIvUm1kLmZpbGVzL1BUQ0wuYmF0Y2guUmRhdGEiKSkKYmF0Y2ggPSBiYXRjaCBbb3JkZXIoYmF0Y2gkbmFtZU5FVyksXQpiYXRjaC5zZXJpZXMgPSBhcy52ZWN0b3IoYmF0Y2gkY2VudGVyKQpiYXRjaCRjYW5jZXIgPSAiY2FuY2VyIgoKIyAjIyMgT1BUSU9OQUw6IENIRUNLIEJBVENIIE9OIEZJTkFMLk1PTEVDCiMgCiMgI21vZCA9IG1vZGVsLm1hdHJpeCh+YXMuZmFjdG9yKGNlbnRlciksIGRhdGE9YmF0Y2gpCiMgbW9kID0gbW9kZWwubWF0cml4KH5hcy5mYWN0b3IoZmluYWwubW9sZWMpLCBkYXRhPWRlc2lnbikKIyBtb2QwID0gbW9kZWwubWF0cml4KH4xLCBkYXRhPSBiYXRjaCkKIyBsaWJyYXJ5KHN2YSkKIyBuLnN2ID0gbnVtLnN2KGFkai5kYXRhLG1vZCxtZXRob2Q9ImxlZWsiKQojIHN2b2JqID0gc3ZhKGFkai5kYXRhLG1vZCxtb2QwLG4uc3Y9bi5zdikKIyAKIyBwVmFsdWVzID0gZi5wdmFsdWUoYWRqLmRhdGEsbW9kLG1vZDApCiMgcVZhbHVlcyA9IHAuYWRqdXN0KHBWYWx1ZXMsbWV0aG9kPSJCSCIpCiMgbW9kU3YgPSBjYmluZChtb2Qsc3ZvYmokc3YpCiMgbW9kMFN2ID0gY2JpbmQobW9kMCxzdm9iaiRzdikKIyBwVmFsdWVzU3YgPSBmLnB2YWx1ZShhZGouZGF0YSxtb2RTdixtb2QwU3YpCiMgcVZhbHVlc1N2ID0gcC5hZGp1c3QocFZhbHVlc1N2LG1ldGhvZD0iQkgiKQoKIyMjIGVuZCBvZiBjaG9pY2UgMQoKIyMjIGNob2ljZSAyOiBnZW5lcmF0ZSB5b3VyIG93biBhZmZ5IG9iamVjdCBhbmQgY3VzdG9tIGRhdGEKCiMgZG93bmxvYWQgQ0VMIGZpbGVzIGZyb20gR0VPIHNlcmllcyBHU0U2MzM4LCBHU0UxOTA2NywgR1NFMTkwNjksIEdTRTQwMTYwLCBHU0U1ODQ0NSwgR1NFNjU4MjMgYW5kIEVCSSBzZXJpZXMgRVRBQk03MDIsIEVUQUJNNzgzCiMgR1NNMzY4NTgwLkNFTCwgR1NNMzY4NTgyLkNFTCwgR1NNMzY4NTg0LkNFTCwgR1NNMzY4NTg2LkNFTCwgR1NNMzY4NTg5LkNFTCwgR1NNMzY4NTkxLkNFTCwgR1NNMzY4NTk0LkNFTCwgR1NNNDcyMTY0LkNFTCwgR1NNMTQxMTI3OC5DRUwsIEdTTTE0MTEyODQuQ0VMLCBHU00xNDExMjg1LkNFTCwgR1NNMTQxMTI4Ny5DRUwsIEdTTTE0MTEzNTUuQ0VMLCBHU00xNDExMzY0LkNFTCwgR1NNMTQxMTM2OC5DRUwsIEdTTTE0MTE0MjUuQ0VMLCBHU00xNDExNDI3LkNFTCBleGNsdWRlZCBmcm9tIHRoZSBhbmFseXNpcyAoc2VlIE1ldGhvZHMgZm9yIGV4cGxhaW5hdGlvbiIpCiMjIyBjZWxmaWxlcyA8LSBkaXIoIn4vRG9jdW1lbnRzL0RBVEkvUFRDTC5ub3MvR1NFNjMzOC1HU0UxOTA2Ny1HU0UxOTA2OS1HU0U0MDE2MC1HU0U1ODQ0NS1HU0U2NTgyMy1FVEFCTTcwMi1FVEFCTTc4My8iLCBwYXR0ZXJuID0gIi5DRUwiKQojIyMgbGlicmFyeShhZmZ5KQojIyMgZ3NldCA9IGp1c3RSTUEoY2VsZmlsZS5wYXRoID0gIi9Vc2Vycy9lbWFnZW5lL0RvY3VtZW50cy9EQVRJL1BUQ0wubm9zL0dTRTYzMzgtR1NFMTkwNjctR1NFMTkwNjktR1NFNDAxNjAtR1NFNTg0NDUtR1NFNjU4MjMtRVRBQk03MDItRVRBQk03ODMvIiwgIyMjIGZpbGVuYW1lcyA9IGNlbGZpbGVzLCBzYW1wbGVOYW1lcyA9IGdzdWIoIi5DRUwiLCIiLCBjZWxmaWxlcyksIGNkZm5hbWUgPSAiaGd1MTMzcGx1czJoc2VudHJlemdjZGYiKQojIyMgZ2VuZUV4cHIgPSBleHBycyhnc2V0KQojIyMgYmF0Y2ggYWRqdXN0bWVudAojIyMgbGlicmFyeShzdmEpICAKIyMjICMgaW1wb3J0IGJhdGNoIGFuZCByZS1vcmRlciBhY2NvcmRpbmdseQojIyMgbG9hZCgiLi9SbWQuZmlsZXMvUFRDTC5iYXRjaC5SZGF0YSIpCiMjIyBiYXRjaCA9IGJhdGNoIFtvcmRlcihyb3duYW1lcyhiYXRjaCkpLF0KIyMjIGJhdGNoLnNlcmllcyA9IGFzLnZlY3RvcihiYXRjaCRjZW50ZXIpCiMjIyBnZW5lRXhwck5FVyA9IGdlbmVFeHByIFsgLCBvcmRlcihjb2xuYW1lcyhnZW5lRXhwcikpIF0KIyMjIGdlbmVFeHByTkVXID0gZ2VuZUV4cHJORVdbZ3JlcCgiQUZGWCIscm93bmFtZXMoZ2VuZUV4cHJORVcpLCBpbnZlcnQ9VFJVRSksXQojIyMgIyBjaGVjayBvcmRlciBjb3JyZXNwb25kZW5jZSBhbmQsIGlmIGNvcnJlY3QsIGFkanVzdCBkYXRhCiMjIyBpZiAoYWxsKGNvbG5hbWVzKGdlbmVFeHByTkVXKSA9PSByb3duYW1lcyhiYXRjaCkpKSB7CiMjIyAgIGFkai5kYXRhID0gQ29tQmF0IChnZW5lRXhwck5FVywgYmF0Y2guc2VyaWVzLCBtb2QgPSBOVUxMLCBwYXIucHJpb3IgPSBUUlVFLCBwcmlvci5wbG90cyA9IFRSVUUpCiMjIyB9IGVsc2UgewojIyMgICBjYXQoIkVycm9yOiBjb2xuYW1lcyBhbmQgYmF0Y2ggZGlkIG5vdCBjb3JyZXNwb25kIikKIyMjIH0KIyMjIGdlbmVFeHByID0gYWRqLmRhdGEKIyMjIGNvbG5hbWVzKGdlbmVFeHByKSA9IGFzLnZlY3RvcihiYXRjaCRuYW1lTkVXKQojIyMgZW5kIG9mIGNob2ljZSAyCmBgYAoKIyMgQ2xpbmljYWwgRGF0YQoKVXBsb2FkIHBheiBpbmZvIHdpdGggY2xpbmljYWwgYW5kIG11dGF0aW9uYWwgZGF0YQoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CnB0cy5pbmZvLmRhdGEgPC0gcmVhZC50YWJsZSgiLi9SbWQuZmlsZXMvNTQxX3Bhel9pbmZvX01VVC50eHQiLCBzZXA9Ilx0IiwgaGVhZGVyPVRSVUUsIGNoZWNrLm5hbWVzPUZBTFNFLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKIyBjdXN0b21pemUgY29sb3JzIGZvciBjYXRlZ29yaWVzCmxldmVscyhhcy5mYWN0b3IocHRzLmluZm8uZGF0YSRmaW5hbC5tb2xlYykpCiMgIkFJVEwiICAgICAiQUxDTC5uZWciICJBTENMLnBvcyIgIkFUTEwiICAgICAiTktUIiAgICAgICJQVENMLm5vcyIgIlQuQ0QzMCIgICAiVC5DRDQiICAgICJULkNEOCIgICAgIlQuRFIiICAgICAiVC5yZWciICAgICJUQ1ItSEwiICAKY29sb3J6ID0gYygiYmxhY2siLCAieWVsbG93IiwiZG9kZ2VyYmx1ZTIiLCJicm93bjIiLCJkYXJrb3JjaGlkMSIsICJvcmFuZ2UiLCAiZ3JleTQyIiwgImdyZXk1MiIsImdyZXk2MiIsImdyZXk3MiIsImdyZXk4MiIsImdyZXk5MiIpCnRlbXAgPSBzcGxpdCAoICBwdHMuaW5mby5kYXRhJHNhbXBsZS5uYW1lTkVXLCBwdHMuaW5mby5kYXRhJGZpbmFsLm1vbGVjICkKY29sb3J4ID0gY29sbmFtZXMoZ2VuZUV4cHIpCmxlbmd0aChjb2xvcnopCmxlbmd0aCh0ZW1wKQpmb3IgKGkgaW4gMTpsZW5ndGgoY29sb3J6KSkgY29sb3J4IFsgd2hpY2goY29sb3J4ICVpbiUgdW5saXN0KHRlbXBbaV0pKSBdID0gY29sb3J6W2ldCmxpYnJhcnkoZ3Bsb3RzKQpjb2xvcnggPSBjb2wyaGV4KGNvbG9yeCkKCiMjIyBidWlsZCBkZXNpZ24gbWF0cml4IGFuZCB0cmFuc2Zvcm0gdG8gbnVtZXJpY2FsIApkZXNpZ24gPC0gcHRzLmluZm8uZGF0YVssYygxOjIsNjo4LDE0OjE3KV0Kcm93bmFtZXMoZGVzaWduKTwtIGRlc2lnblssMV0KZGVzaWduPC0gZGVzaWduWywtYygxOjIpXQojZGVzaWduPC1uYS5vbWl0KGRlc2lnbikgIyMjIHNlbGVjdCBvbnlsIHBhdGllbnRzIHdpdGggYWxsIG11dGF0aW9ucyBkYXRhIGF2YWlsYWJsZSAobj01MykKZGVzaWduJGFnZTwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRlc2lnbiRhZ2UpKQpkZXNpZ24kYWdlPC0gZGVzaWduJGFnZSAtIG1lZGlhbihkZXNpZ24kYWdlKQpkZXNpZ25bZGVzaWduID09ICJXVCJdIDwtIDAKZGVzaWduW2Rlc2lnbiA9PSAiTVVUIl0gPC0gMQpkZXNpZ24kZmluYWwubW9sZWNbZGVzaWduJGZpbmFsLm1vbGVjPT0iQUlUTCJdIDwtIDAKZGVzaWduJGZpbmFsLm1vbGVjW2Rlc2lnbiRmaW5hbC5tb2xlYz09IlBUQ0wubm9zIl0gPC0gMQpkZXNpZ24kZmluYWwubW9sZWNbZGVzaWduJGZpbmFsLm1vbGVjPT0iQUxDTC5uZWciXSA8LSAyCmRlc2lnbiRmaW5hbC5tb2xlY1tkZXNpZ24kZmluYWwubW9sZWM9PSJBTENMLnBvcyJdIDwtIDMKZGVzaWduJGZpbmFsLm1vbGVjW2Rlc2lnbiRmaW5hbC5tb2xlYz09IkFUTEwiXSA8LSA0CmRlc2lnbiRmaW5hbC5tb2xlY1tkZXNpZ24kZmluYWwubW9sZWM9PSJOS1QiXSA8LSA1CmRlc2lnbiRmaW5hbC5tb2xlY1s0Nzc6NTQxXSA8LSA2CmRlc2lnbiRnZW5kZXJbZGVzaWduJGdlbmRlcj09Ik0iXSA8LSAxCmRlc2lnbiRnZW5kZXJbZGVzaWduJGdlbmRlcj09IkYiXSA8LSAwCmRlc2lnbiRhZ2UgPSBOVUxMCmFsbChwdHMuaW5mby5kYXRhJHNhbXBsZS5uYW1lTkVXID09IGJhdGNoJG5hbWVORVcpIAoKCmBgYAoKIyBQaWUgQ2hhcnQgd2l0aCBQZXJjZW50YWdlcwoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CgpzbGljZXMgPC0gdGFibGUocHRzLmluZm8uZGF0YSRmaW5hbC5tb2xlYykKbGJscyA8LSBuYW1lcyh0YWJsZShwdHMuaW5mby5kYXRhJGZpbmFsLm1vbGVjKSkKcGN0IDwtIHJvdW5kKHNsaWNlcy9zdW0oc2xpY2VzKSoxMDApCmxibHMgPC0gcGFzdGUobGJscywgIjogIiwgc2xpY2VzLCAiICgiLCBwY3QsICIlKSIsIHNlcD0iIiApICMgYWRkIHBlcmNlbnRzIHRvIGxhYmVscwojcGRmKCJGaWd1cmVfMWFfcGllX3Bsb3QucGRmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpwYXIobWZyb3c9YygxLDEpKQpwYXIobWFyPWMoMywzLDMsMyksIHhwZD1GKQpwaWUoc2xpY2VzLGxhYmVscyA9IGxibHMsIGluaXQuYW5nbGUgPSAwLCBjb2w9Y29sb3J6LCBtYWluPSIiLCBjZXg9MC42LCByYWRpdXM9MC44KQojZGV2Lm9mZigpCgoKCmBgYAoKCgojIFBDQQoKYGBge3IsZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQoKIyBhcHBseSB2YXJpYXRpb25hbCBmaWx0ZXIKCmFmYzIgPSBhdmVmYyhnZW5lRXhwciwgbG9nPVRSVUUsIHJlcGxhY2U9RkFMU0UpCmRhdGE1NDFleHBycy52ZiA9IGdlbmVFeHByIFthZmMyID49IDIsIF0KZGltKGRhdGE1NDFleHBycy52ZgkpCiMgcmV0cnkgUENBIG9uIHNob3J0ZWQgZ2VuZSBsaXN0CmRhdGE1NDFtID0gdChhcy5tYXRyaXgoZGF0YTU0MWV4cHJzLnZmKSkKcGNhPC1wcmNvbXAoZGF0YTU0MW0sc2NhbGU9VCkKbWZyb3czZChuciA9IDEsIG5jID0gMSwgc2hhcmVkTW91c2UgPSBUKSAgCnBsb3QzZChwY2EkeCxyZ2wudXNlPUYsY29sPWNvbG9yeCxzaXplPTAuNix0eXBlPSJzIikKcmdsd2lkZ2V0KCkKYGBgCgojIEhlYXRtYXAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQoKbWF0ID0gYXMubWF0cml4KGRhdGE1NDFleHBycy52ZikKYmFzZV9tZWFuID0gcm93TWVhbnMobWF0KQptYXRfc2NhbGVkID0gdChhcHBseShtYXQsIDEsIHNjYWxlKSkKdHlwZXMgPSBwdHMuaW5mby5kYXRhJGZpbmFsLm1vbGVjCmNvbG9yLmFubm90ID0gY29sMmhleChjb2xvcnopOyBuYW1lcyAoY29sb3IuYW5ub3QpPSBuYW1lcyh0ZW1wKSAgCmhhID0gSGVhdG1hcEFubm90YXRpb24oZGYgPSBkYXRhLmZyYW1lKHR5cGUgPSB0eXBlcykgLCBjb2wgPSBsaXN0KHR5cGUgPSBjKCBjb2xvci5hbm5vdCApICkgKQpoYUBhbm5vX2xpc3RbWzFdXUBjb2xvcl9tYXBwaW5nQGNvbG9ycyA9IGNvbDJoZXgoY29sb3J6KQpuYW1lcyhoYUBhbm5vX2xpc3RbWzFdXUBjb2xvcl9tYXBwaW5nQGNvbG9ycykgPSBuYW1lcyh0ZW1wKQpodCA9IEhlYXRtYXAobWF0X3NjYWxlZCwgbmFtZSA9ICJleHByZXNzaW9uIiwga20gPSA3LCBjbHVzdGVyaW5nX21ldGhvZF9jb2x1bW5zID0gIndhcmQuRCIsIGNvbCA9IGNvbG9yUmFtcDIoYygtMSwgMCwgMSksIGMoImdyZWVuIiwgIndoaXRlIiwgInJlZCIpKSwgdG9wX2Fubm90YXRpb24gPSBoYSwgdG9wX2Fubm90YXRpb25faGVpZ2h0ID0gdW5pdCg0LCAibW0iKSwgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwgc2hvd19jb2x1bW5fbmFtZXMgPSBGQUxTRSkKY29sdW1uX29yZGVyKGh0KQpgYGAKCgojIENoZWNrIHJlbGF0aXZlIGxvZyBleHByZXNzaW9uIGFmdGVyIGJhdGNoIGNvcnJlY3Rpb24KCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpybGUuY3VzdG9tID0gZnVuY3Rpb24gKGEsIGxvZ2dlZDIgPSBUUlVFLCBmaWxlID0gTlVMTCwgY29sb3Jib3g9IE5VTEwsIGxhYmVscz1OVUxMICwgbGVnZW5kID0gTlVMTCApIHsKICAgIGEubSA8LSBhcHBseShhLDEsbWVkaWFuKQppZiAobG9nZ2VkMikgewogICAgZm9yIChpIGluIDE6ZGltKGEpWzJdKSB7CiAgICAgICAgIGEgWyxpXSA8LSAgYSBbLGldIC0gYS5tCiAgICB9CiAgICB9IGVsc2UgewogICAgICAgIGZvciAoaSBpbiAxOmRpbShhKVsyXSkgewogICAgICAgICBhIFssaV0gPC0gIGxvZyAoYSBbLGldIC8gYS5tICkKICAgIH0KICAgIH0KICAgIyBwbmcoZmlsZSwxMDI0MCwzODQwKQogICAgcGFyKG1hcj1jKDEwLDQsNiwyKSkKICAgIGJveHBsb3QgKGEsIHlsaW09IGMoLTUsNSksIG91dGxpbmU9RiwgY29sPWNvbG9yYm94LCB4bGFiPSJwdHMiLCBuYW1lcz1sYWJlbHMsIGxhcz0yLCBjZXguYXhpcyA9IDEuNSwgbWFpbj0iUkxFIiwgeGxpbSA9IGMoMSw2MDApLCBjZXgubWFpbiA9IDUgKQogICAgbGVnZW5kKCJib3R0b21yaWdodCIsbGVnZW5kID0gYyhsZXZlbHMoYXMuZmFjdG9yKHB0cy5pbmZvLmRhdGEkZmluYWwubW9sZWMpKSksICAgCiAgICAgIGZpbGwgPSBjb2xvcnosICMgNjoxIHJlb3JkZXJzIHNvIGxlZ2VuZCBvcmRlciBtYXRjaGVzIGdyYXBoCiAgICAgIHRpdGxlID0gIkxlZ2VuZCIsCiAgICAgIGNleCA9IDUpCiAgIyAgZGV2Lm9mZigpCgogICAgYS5jID0gYXBwbHkoYSwgMiwgc3RhdHM6OnF1YW50aWxlKQogICAgcmV0dXJuKGEuYykKfQoKI3JsZS5tZWRpYW5zID0gcmxlLmN1c3RvbShnZW5lRXhwciwgY29sb3Jib3g9Y29sb3J4LCBmaWxlPSIuL1JMRS41NDEucG5nIiwgbGFiZWxzPXB0cy5pbmZvLmRhdGEkc2FtcGxlLm5hbWVORVcgKQojcGxvdChybGUubWVkaWFuc1szLF0sIHR5cGU9ImwiLCB4bGFiPSJwdHMiLCB5bGFiPSJSTEUgbWVkaWFuIiApCnJsZS5tZWRpYW5zID0gcmxlLmN1c3RvbShnZW5lRXhwciwgY29sb3Jib3g9Y29sb3J4LCBmaWxlPSIuL1JMRS41NDEucG5nIiwgbGFiZWxzPXB0cy5pbmZvLmRhdGEkc2FtcGxlLm5hbWVORVcgKQpwbG90KHJsZS5tZWRpYW5zWzMsXSwgdHlwZT0ibCIsIHhsYWI9InB0cyIsIHlsYWI9IlJMRSBtZWRpYW4iICkKCmBgYAoKCiMjIEZpbmFsIEdlbmUgRXhwcmVzc2lvbiBNYXRyaXgKCkRlZmluZSBkZXNpZ24gZmlsZSBhbmQgZmlsdGVyIGdlbmVFeHByIGZvciBwYXRpZW50cyBpbmNsdWRlZCBpbiBkZXNpZ24gZGF0YSBmcmFtZSBhbmQKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpkZXNpZ24gPC0gcHRzLmluZm8uZGF0YVssYygxOjIsNjo4LDE0OjE3KV0Kcm93bmFtZXMoZGVzaWduKTwtIGRlc2lnblssMV0KZGVzaWduPC0gZGVzaWduWywtYygxOjIpXQpkZXNpZ248LW5hLm9taXQoZGVzaWduKSAjIyMgIHNlbGVjdCBvbnlsIHBhdGllbnRzIHdpdGggYWxsIG11dGF0aW9ucyBkYXRhIGF2YWlsYWJsZSAobj01MykKZGVzaWduJGFnZTwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRlc2lnbiRhZ2UpKQpkZXNpZ24kYWdlPC0gZGVzaWduJGFnZSAtIG1lZGlhbihkZXNpZ24kYWdlKQpkZXNpZ25bZGVzaWduID09ICJXVCJdIDwtIDAKZGVzaWduW2Rlc2lnbiA9PSAiTVVUIl0gPC0gMQpkZXNpZ24kZmluYWwubW9sZWNbZGVzaWduJGZpbmFsLm1vbGVjPT0iQUlUTCJdIDwtIDAKZGVzaWduJGZpbmFsLm1vbGVjW2Rlc2lnbiRmaW5hbC5tb2xlYz09IlBUQ0wubm9zIl0gPC0gMQpkZXNpZ24kZ2VuZGVyW2Rlc2lnbiRnZW5kZXI9PSJNIl0gPC0gMQpkZXNpZ24kZ2VuZGVyW2Rlc2lnbiRnZW5kZXI9PSJGIl0gPC0gMApkZXNpZ24kb2Zmc2V0IDwtIHJlcCgxLCBucm93KGRlc2lnbikpCmRlc2lnbjwtZGVzaWduWyxjKDgsMTo3KV0KCmFsbChwdHMuaW5mby5kYXRhJHNhbXBsZS5uYW1lTkVXID09IGNvbG5hbWVzKGdlbmVFeHByKSkgIyMgY2hlY2sgY29ycmVzcG9uZGVuY2UKIyBnZW5lRXhwciA9IGdlbmVFeHByIFsgLCBvcmRlciAocHRzLmluZm8uZGF0YSRnZW8uaWQpXSAjIyMgZG8gb25seSB0byBzZXQgY29ycmVzcG9uZGVuY2UgaW4gY2FzZSBvZiBjdXN0b20gcHJvY2VkdXJlCiMgY29sbmFtZXMoZ2VuZUV4cHIpID0gcHRzLmluZm8uZGF0YSRzYW1wbGUubmFtZU5FVyBbIG9yZGVyIChwdHMuaW5mby5kYXRhJGdlby5pZCldCgpnZW5lRXhwcjI8LSAoZ2VuZUV4cHJbLCByb3duYW1lcyhkZXNpZ24pXSkKZ2VuZUV4cHIyPC0gZGF0YS5tYXRyaXgoZ2VuZUV4cHIyLCByb3duYW1lcy5mb3JjZSA9IE5BKQpkZXNpZ248LSBkYXRhLm1hdHJpeChkZXNpZ24sIHJvd25hbWVzLmZvcmNlID0gTkEpCmBgYAoKIyMgTW9kZWwgZml0dGluZwpXZSB1c2UgdGhlIGxtRml0IGZ1bmN0aW9uIGZyb20gdGhlIGxpbW1hIHBhY2thZ2UuIFRoaXMgY29tZXMgd2l0aCBhIHdob2xlIHNlcmllcyBvZiBwb3dlcmZ1bCBhbmQgcmVsaWFibGUgdGVzdHMuCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KZ2xtID0gbG1GaXQoZ2VuZUV4cHIyWyxyb3duYW1lcyhkZXNpZ24pXSwgZGVzaWduID0gZGVzaWduICkKZ2xtID0gZUJheWVzKGdsbSkKRi5zdGF0IDwtIGNsYXNzaWZ5VGVzdHNGKGdsbVssLTFdLGZzdGF0Lm9ubHk9VFJVRSkKZ2xtJEYgPC0gYXMudmVjdG9yKEYuc3RhdCkKZGYxIDwtIGF0dHIoRi5zdGF0LCJkZjEiKQpkZjIgPC0gYXR0cihGLnN0YXQsImRmMiIpCmlmKGRmMlsxXSA+IDFlNil7CiAgZ2xtJEYucC52YWx1ZSA8LSBwY2hpc3EoZGYxKmdsbSRGLGRmMSxsb3dlci50YWlsPUZBTFNFKQp9ZWxzZQogIGdsbSRGLnAudmFsdWUgPC0gcGYoZ2xtJEYsZGYxLGRmMixsb3dlci50YWlsPUZBTFNFKQoKc2V0LnNlZWQoMTIzNDU2NzgpCnJsbSA8LSBsbUZpdChnZW5lRXhwclsscm93bmFtZXMoZGVzaWduKV0sIGFwcGx5KGRlc2lnbiwgMiwgc2FtcGxlKSkKcmxtIDwtIGVCYXllcyhybG0pCkYuc3RhdCA8LSBjbGFzc2lmeVRlc3RzRihybG1bLC0xXSxmc3RhdC5vbmx5PVRSVUUpCnJsbSRGIDwtIGFzLnZlY3RvcihGLnN0YXQpCmRmMSA8LSBhdHRyKEYuc3RhdCwiZGYxIikKZGYyIDwtIGF0dHIoRi5zdGF0LCJkZjIiKQppZihkZjJbMV0gPiAxZTYpewogIHJsbSRGLnAudmFsdWUgPC0gcGNoaXNxKGRmMSpybG0kRixkZjEsbG93ZXIudGFpbD1GQUxTRSkKfWVsc2UKICBybG0kRi5wLnZhbHVlIDwtIHBmKHJsbSRGLGRmMSxkZjIsbG93ZXIudGFpbD1GQUxTRSkKRi5zdGF0IDwtIGNsYXNzaWZ5VGVzdHNGKGdsbVssMjo1XSxmc3RhdC5vbmx5PVRSVUUpCmRmMSA8LSBhdHRyKEYuc3RhdCwiZGYxIikKZGYyIDwtIGF0dHIoRi5zdGF0LCJkZjIiKQpGLnAudmFsdWUgPC0gcGNoaXNxKGRmMSpGLnN0YXQsZGYxLGxvd2VyLnRhaWw9RkFMU0UpClIuc3RhdCA8LSBjbGFzc2lmeVRlc3RzRihybG1bLDI6NV0sZnN0YXQub25seT1UUlVFKQpSYWxsID0gMSAtIDEvKDEgKyBnbG0kRiAqIChuY29sKGRlc2lnbiktMSkvKG5yb3coZGVzaWduKS1uY29sKGRlc2lnbikpKQpSZ2VuZXRpY3MgPSAxIC0gMS8oMSArIEYuc3RhdCAqIDQvKG5yb3coZGVzaWduKS1uY29sKGRlc2lnbikpKQpQZ2VuZXRpY3MgPSAxIC0gMS8oMSArIFIuc3RhdCAqIDQvKG5yb3coZGVzaWduKS1uY29sKGRlc2lnbikpKQpuYW1lcyhSZ2VuZXRpY3MpIDwtIG5hbWVzKFBnZW5ldGljcykgPC0gbmFtZXMoUmFsbCkgPC0gIHJvd25hbWVzKGdlbmVFeHByKQoKYGBgCgojIyBEaWZmZXJlbnRpYWxseSBFeHByZXNzZWQgR2VuZXMKCmBgYHtyIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNywgd2FybmluZz1GQUxTRX0KcGFyKGJ0eT0ibiIsIG1ncCA9IGMoMiwuMzMsMCksIG1hcj1jKDMsMi41LDEsMSkrLjEsIGxhcz0xLCB0Y2w9LS4yNSwgeHBkPU5BKQpkIDwtIGRlbnNpdHkoUGdlbmV0aWNzLGJ3PTFlLTMpCmYgPC0gNDAgI25yb3coZ2V4cHIpLzUxMgoKI3BkZigiRmlndXJlXzJhX01BWS5wZGYiLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA3KQpwYXIobWZyb3c9YygxLDEpKQpwYXIobWFyPWMoOCw1LDUsNSksIHhwZD1GKQpwbG90KGQkeCwgZCR5ICogZiwgY29sPSdncmV5JywgeGxhYj1leHByZXNzaW9uKHBhc3RlKCJFeHBsYWluZWQgdmFyaWFuY2UgcGVyIGdlbmUgIiwgUl4yKSksIG1haW49IiIsIGx3ZD0yLCB0eXBlPSJsIiwgeWxhYj0iIiwgeGxpbT1jKDAsMSksIGNleC5heGlzPTEuMiwgY2V4LmxhYj0xLjUsIGJ0eT0ibiIpCnRpdGxlKHlsYWI9IkRlbnNpdHkiLCBsaW5lPTIuNSwgY2V4LmxhYj0xLjUpCmQgPC0gZGVuc2l0eShSZ2VuZXRpY3MsIGJ3PTFlLTMpCnIgPC0gbWluKFJnZW5ldGljc1twLmFkanVzdChGLnAudmFsdWUsIkJIIik8MC4wMV0pICMjIyMjIyMjIHRocmVzaG9sZCB0byBzZWxlY3QgNDEyIGdlbmVzCngwIDwtIHdoaWNoKGQkeD5yKQpwb2x5Z29uKGQkeFtjKHgwWzFdLHgwKV0sIGMoMCxkJHlbeDBdKSogZiwgY29sPXBhc3RlKHNldDFbMV0sIjQ0IixzZXA9IiIpLCBib3JkZXI9TkEpCmxpbmVzKGQkeCwgZCR5KiBmLCBjb2w9c2V0MVsxXSwgbHdkPTIpCnRleHQoZCR4W3gwWzFdXSwgZCR5W3gwWzFdXSpmICsyMCwgcG9zPTQsIHBhc3RlKHN1bShSZ2VuZXRpY3MgPiByKSwgImdlbmVzIHEgPCAwLjAxIikpCmxlZ2VuZCgidG9wcmlnaHQiLCBidHk9Im4iLCBjb2w9YyhzZXQxWzFdLCAiZ3JleSIpLCBsdHk9MSwgYygiT2JzZXJ2ZWQiLCJSYW5kb20iKSwgbHdkPTIpCiNkZXYub2ZmKCkKCmdsbVByZWRpY3Rpb24gPC0gZ2xtJGNvZWZmaWNpZW50cyAlKiUgdChkZXNpZ24pCnJsbVByZWRpY3Rpb24gPC0gcmxtJGNvZWZmaWNpZW50cyAlKiUgdChkZXNpZ24pCmBgYAoKUHJpbnQgc2lnbmZpY2lhbnQgZ2VuZXMKCmBgYHtyfQoKa2s8LWFzLmRhdGEuZnJhbWUoKHAuYWRqdXN0KEYucC52YWx1ZSwiQkgiKTwwLjAxKSkKa2skZ2VuZTwtIHJvd25hbWVzKGtrKQpjb2xuYW1lcyhraylbMV08LSJjb2RlIgprazI8LWtrW2trJGNvZGU9PSJUUlVFIixdCiMjIyBzb3J0KGtrMiRnZW5lKSAjIyMjIyBpZiB5b3Ugd2FudCB0byBwcmludCB0aGUgZW50aXJlIGxpc3Qgb2YgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzCgpgYGAKCiMjIFNpZ25pZmljYW50IGVmZmVjdHMgcGVyIGNvdmFyaWF0ZQoKRXh0cmFjdCB0aGUgbGlzdCBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgYnkgbXV0YXRpb25zCgpgYGB7cn0KIyMjIGN1c3RvbWl6ZSBjb2xvcnMgaW4gY29sTXV0YXRpb25zCiMgY29sTXV0YXRpb25zID0gYyhicmV3ZXIucGFsKDgsIlNldDEiKVstNl0sIHJldihicmV3ZXIucGFsKDgsIkRhcmsyIikpLCBicmV3ZXIucGFsKDcsIlNldDIiKSlbYygxOjEyLDE2OjE5LDEzOjE1KV0KIyBvIDwtIG9yZGVyKGFwcGx5KGNvbDJyZ2IoY29sTXV0YXRpb25zKSwyLHJnYjJoc3YpWzEsXSkKIyBjb2xNdXRhdGlvbnMgPC0gY29sTXV0YXRpb25zW3JldihvKV1bKDQqMToxOSArMTUpICUlIDE5ICsgMV1bMTo3XQpjb2xNdXRhdGlvbnMgPSBjb2wyaGV4KGMoIm1hZ2VudGEiLCAicHVycGxlIiwiZ3JheTYwIiwicmVkIiwibGlnaHRibHVlIiwiZ3JlZW4iLCJvcmFuZ2UiKSkKbmFtZXMoY29sTXV0YXRpb25zKSA8LSBjb2xuYW1lcyhkZXNpZ24pWy0xXQoKZ2VuZV9jb2RlPC0ga2syJGdlbmUKdGFiPU5VTEwKZm9yKGkgaW4gKDE6bGVuZ3RoKGtrMiRnZW5lKSkpCnsKICBnZW5lX3NpbmdsZTwtIGdlbmVfY29kZVtpXQogIHkgPC0gZ2xtJGNvZWZmaWNpZW50c1tnZW5lX3NpbmdsZSwtMV0rZ2xtJGNvZWZmaWNpZW50c1tnZW5lX3NpbmdsZSwxXQogIHcgPC0gZ2xtJHAudmFsdWVbZ2VuZV9zaW5nbGUsLTFdIDwgMC4wNQogIGludDwtYyhnZW5lX3NpbmdsZSwgYXMuY2hhcmFjdGVyKHcpKQogIHRhYjwtIHJiaW5kKHRhYiwgaW50KQp9CnJvd25hbWVzKHRhYik8LXNlcSgxOm5yb3codGFiKSkKY29sbmFtZXModGFiKTwtIGMoImdlbmUiLGNvbG5hbWVzKGRlc2lnbilbLTFdKQoKIyBXcml0ZSB0byBkaXNrIGEgZmlsZSB3aXRoIGFsbCBzaWduaWZpY2FudCBnZW5lcwojd3JpdGUudGFibGUodGFiLCAidGFibGVfZGlmZmVyZW50aWFsbHlfZXhwcmVzc2VkX2dlbmUudHh0IixzZXA9Ilx0IiwgcXVvdGU9Riwgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gVCkKYGBgCgpFeGFtcGxlIG9mIGV4dHJhY3Rpb24KCmBgYHtyIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gN30KCiAjIHRlbXBfbmFtZSA9IHVuaXF1ZShnZXRCTSggYXR0cmlidXRlcyA9IGMoImVuc2VtYmxfdHJhbnNjcmlwdF9pZCIsICJlbnRyZXpnZW5lIiwgImV4dGVybmFsX2dlbmVfbmFtZSIpLCBmaWx0ZXJzID0gImVudHJlemdlbmUiLCB2YWx1ZXMgPSBnc3ViKCJfYXQiLCIiLGdlbmVfc2luZ2xlKSwKICMgbWFydCA9IGVuc2VtYmwpJGV4dGVybmFsX2dlbmVfbmFtZSkKICAjcGRmKCJGaWd1cmVfMmIucGRmIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNykKICBwYXIobWZyb3c9YygxLDEpKQogIHBhcihtYXI9YygxMCw4LDUsNSksIHhwZD1GKQogIHBhcihidHk9Im4iLCBtZ3AgPSBjKDEuNSwuMzMsMCksbGFzPTEsIHRjbD0tLjI1LCB4cGQ9RikKICB0ZW1wX25hbWU8LSAiWUFQMSIKICBwbG90KGdsbVByZWRpY3Rpb25bZ2VuZV9zaW5nbGUsXSwgZ2VuZUV4cHJbZ2VuZV9zaW5nbGUscm93bmFtZXMoZGVzaWduKV0sIHlsYWI9IiIsIHhsYWI9IiIsCiAgICAgICBwY2g9MTYsIGNleD0xLCBjZXguYXhpcz0xLjIsIGNleC5sYWI9MS41KQogIHRpdGxlKHlsYWI9KHBhc3RlKCJPYnNlcnZlZCAiLHRlbXBfbmFtZSwgIiBleHByZXNzaW9uIikpLCBsaW5lPTIuNSwgY2V4LmxhYj0xLjUpCiAgIHRpdGxlKCB4bGFiPShwYXN0ZSgiUHJlZGljdGVkICIsdGVtcF9uYW1lLCAiIGV4cHJlc3Npb24iKSksIGxpbmU9Mi41LCBjZXgubGFiPTEuNSkKICBhYmxpbmUoMCwxKQogIHUgPC0gcGFyKCJ1c3IiKQogIHBhcih4cGQ9TkEpCiAgeSA8LSBnbG0kY29lZmZpY2llbnRzW2dlbmVfc2luZ2xlLC0xXStnbG0kY29lZmZpY2llbnRzW2dlbmVfc2luZ2xlLDFdCiAgdSA8LSBwYXIoInVzciIpCiAgeDAgPC0gcmVwKHVbM10rMSxuY29sKGRlc2lnbiktMSkKICB5MCA8LSB1WzRdICsgMC4wNSoodVs0XS11WzNdKSAtIHJhbmsoLXkpL2xlbmd0aCh5KSAqICh1WzRdLXVbM10pLzEuMgogIGQgPC0gZGVuc2l0eSh5KQogIGxpbmVzKGQkeCwgZCR5LzUrMSt1WzNdLCBjb2w9ImdyZXkiKQogIGxpbmVzKGQkeCwgLWQkeS81KzErdVszXSwgY29sPSJncmV5IikKcG9pbnRzKHg9eSwgeT14MCt2aW9saW5KaXR0ZXIoeSwgbWFnbml0dWRlPTAuMjUpJHksIGNvbD1jb2xNdXRhdGlvbnMsIHBjaD0xNiwgY2V4PTEuNSkKICB0ZXh0KHg9Z2xtJGNvZWZmaWNpZW50c1tnZW5lX3NpbmdsZSwxXSwgeT0gNS4yLCAiTW9kZWwgY29lZmZpY2llbnRzIiwgY2V4PTAuOCkKbGVnZW5kKCJ0b3BsZWZ0IixuYW1lcyhjb2xNdXRhdGlvbnMpLCBjb2wgPSBjb2xNdXRhdGlvbnMsIGJ0eT0gIm4iLCBjZXggPSAxLjIsIHBjaCA9IDE2KQojZGV2Lm9mZigpCmBgYAoKClBsb3Qgc2lnbmlmaWNhbnQgZWZmZWN0cyBwZXIgY292YXJpYXRlIChxPDAuMDEpCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KdGVzdFJlc3VsdHMgPC0gZGVjaWRlVGVzdHMoZ2xtLCBtZXRob2Q9ImhpZXJhcmNoaWNhbCIsYWRqdXN0Lm1ldGhvZD0iQkgiLCBwLnZhbHVlPTAuMDEpWywtMV0Kc2lnbmlmaWNhbnRHZW5lcyA8LSBzYXBwbHkoMTpuY29sKHRlc3RSZXN1bHRzKSwgZnVuY3Rpb24oail7CiAgYyA8LSBnbG0kY29lZmZpY2llbnRzW3Rlc3RSZXN1bHRzWyxqXSE9MCxqKzFdCiAgdGFibGUoY3V0KGMsIGJyZWFrcz1jKC01LHNlcSgtMS41LDEuNSxsPTcpLDUpKSkKfSkKCmNvbG5hbWVzKHNpZ25pZmljYW50R2VuZXMpIDwtIGNvbG5hbWVzKHRlc3RSZXN1bHRzKQpyb3duYW1lcyh0YWIpPC1jKDE6bnJvdyh0YWIpKQp0YWIyPC0gYXMuZGF0YS5mcmFtZSh0YWIpCnRhYjIkZ2VuZTwtYXMuY2hhcmFjdGVyKGFzLmNoYXJhY3Rlcih0YWIyJGdlbmUpKQp0YWIyJGZpbmFsLm1vbGVjPC1hcy5jaGFyYWN0ZXIoYXMuY2hhcmFjdGVyKHRhYjIkZmluYWwubW9sZWMpKQp0YWIyJFRFVDI8LWFzLmNoYXJhY3Rlcihhcy5jaGFyYWN0ZXIodGFiMiRURVQyKSkKdGFiMiRSSE9BPC1hcy5jaGFyYWN0ZXIoYXMuY2hhcmFjdGVyKHRhYjIkUkhPQSkpCnRhYjIkSURIMjwtYXMuY2hhcmFjdGVyKGFzLmNoYXJhY3Rlcih0YWIyJElESDIpKQp0YWIyJEROTVQzQTwtYXMuY2hhcmFjdGVyKGFzLmNoYXJhY3Rlcih0YWIyJEROTVQzQSkpCgoKIyAgcGRmKCJGaWd1cmVfMmMucGRmIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNykKICBwYXIobWZyb3c9YygxLDEpKQogIHBhcihtYXI9Yyg4LDgsNSw1KSwgeHBkPUYpCgpwYXIobWZyb3c9YygxLDEpKQpwYXIoYnR5PSJuIiwgbWdwID0gYygyLjUsLjMzLDApLCBtYXI9Yyg1LDUuNSw1LDApKy4xLCBsYXM9MiwgdGNsPS0uMjUpCmIgPC0gYmFycGxvdChzaWduaWZpY2FudEdlbmVzLCBsYXM9MiwgeWxhYiA9ICJEaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMiLCBjb2w9YnJld2VyLnBhbCg4LCJSZFlsQnUiKSwgbGVnZW5kLnRleHQ9RkFMU0UgLCBib3JkZXI9MCwgeGF4dD0ibiIsIGNleC5sYWI9MS41KSMsIGNvbCA9IHNldDFbc2ltcGxlLmFubm90W25hbWVzKG4pXV0sIGJvcmRlcj1OQSkKcm90YXRlZExhYmVsKHgwPWItMC4xLCB5MD1yZXAoLTAuNSwgbmNvbChzaWduaWZpY2FudEdlbmVzKSksIGxhYmVscz1jb2xuYW1lcyhzaWduaWZpY2FudEdlbmVzKSwgY2V4PTEuMiwgc3J0PTQ1LCBmb250PWlmZWxzZShncmVwbCgiW1s6bG93ZXI6XV0iLCBjb2xuYW1lcyhkZXNpZ24pKVstMV0sIDEsMyksIGNvbD1jb2xNdXRhdGlvbnMpCnJvdGF0ZWRMYWJlbChiLTAuMSwgY29sU3VtcyhzaWduaWZpY2FudEdlbmVzKSwgY29sU3VtcyhzaWduaWZpY2FudEdlbmVzKSwgcG9zPTMsIGNleD0sIHNydD00NSkjZGV2Lm9mZigpCmNsaXAoMCwzMCwwLDEwMDApCngwIDwtIDcuNQppbWFnZSh4PXgwK2MoMCwwLjgpLCB5PXBhcigidXNyIilbNF0rc2VxKC0xLDEsbD05KSAtNCwgej1tYXRyaXgoMTo4LCBuY29sPTgpLCBjb2w9YnJld2VyLnBhbCg4LCJSZFlsQnUiKSwgYWRkPVRSVUUpCnRleHQoeD14MCsxLjEsIHk9cGFyKCJ1c3IiKVs0XStjKC0xLDAsMSkgLTQsIGZvcm1hdChzZXEoLTEsMSxsPTMpLDIpLCBjZXg9MC42NikKbGluZXMoeD1yZXAoeDArMC45LCAyKSwgeT1wYXIoInVzciIpWzRdK2MoLTEsMSkgLTQpCnNlZ21lbnRzKHgwKzAuOSxwYXIoInVzciIpWzRdICsgMS00LHgwKzAuOTUscGFyKCJ1c3IiKVs0XSArIDEtNCkKc2VnbWVudHMoeDArMC45LHBhcigidXNyIilbNF0gKyAwLTQseDArMC45NSxwYXIoInVzciIpWzRdICsgMC00KQpzZWdtZW50cyh4MCswLjkscGFyKCJ1c3IiKVs0XSArIC0xLTQseDArMC45NSxwYXIoInVzciIpWzRdICsgLTEtNCkKdGV4dCh4MCArIDAuNDUsIHBhcigidXNyIilbNF0gKyAxLjUtNCwgImxvZzIgRkMiLCBjZXg9LjY2KQoKI2Rldi5vZmYoKQoKIyBwYXIoYnR5PSJuIiwgbWdwID0gYygyLjUsLjMzLDApLCBtYXI9YygzLDMuMywzLDApKy4xLCBsYXM9MSwgdGNsPS0uMjUpCiMgdCA8LSB0YWJsZShyb3dTdW1zKGFicyh0ZXN0UmVzdWx0c1ssMTo2XSkpKQojIGIgPC0gYmFycGxvdCh0Wy0xXSx5bGFiPSJEaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMiLCBjb2w9cmV2KGJyZXdlci5wYWwoNywgIlNwZWN0cmFsIilbLSg0OjUpXSksIGJvcmRlcj1OQSkKIyByb3RhdGVkTGFiZWwoYi0wLjEsIHRbLTFdLCB0Wy0xXSwgcG9zPTMsIGNleD0xLCBzcnQ9NDUpCiMgdGl0bGUoeGxhYj0iQXNzb2NpYXRlZCBkcml2ZXJzIiwgbGluZT0yKQoKCgpgYGAKClByaW50IHRoZSBsaXN0IG9mIGRpZmZlcmVudGx5IGV4cHJlc3NlZCBnZW5lcyB1c2luZyB0aGUgRW5zZW1ibCBhbm5vdGF0aW9uCgpgYGB7cn0Kc2VsZWN0X2hpc3Q8LSBwdHMuaW5mby5kYXRhW3B0cy5pbmZvLmRhdGEkZmluYWwubW9sZWMgPT0gIkFJVEwiIHwgIHB0cy5pbmZvLmRhdGEkZmluYWwubW9sZWMgPT0gIlBUQ0wubm9zIixdCmdlbmU8LSBhcy5kYXRhLmZyYW1lKHRlc3RSZXN1bHRzKQpzaWdfZ2VuZXM8LSBnZW5lW2dlbmUkZmluYWwubW9sZWMhPSAwIHxnZW5lJElESDIgIT0gMCB8IGdlbmUkVEVUMiAhPSAwIHwgZ2VuZSRETk1UM0EgIT0gMCB8IGdlbmUkUkhPQSAhPSAwLF0KbGlzdF9nZW5lczwtc29ydChyb3duYW1lcyhzaWdfZ2VuZXMpKSAjIyMjIyBsaXN0IG9mIHNpZ25maWNpYW50IGdlbmVzCmdlbmVhbm5vdGF0aW9uMSA8LSBnZXRCTSggYXR0cmlidXRlcyA9IGMoImVuc2VtYmxfdHJhbnNjcmlwdF9pZCIsICJlbnRyZXpnZW5lIiwgImV4dGVybmFsX2dlbmVfbmFtZSIpLCBmaWx0ZXJzID0gImVudHJlemdlbmUiLCB2YWx1ZXMgPSBnc3ViKCJfYXQiLCIiLGxpc3RfZ2VuZXMpLCBtYXJ0ID0gZW5zZW1ibCkKc29ydCh1bmlxdWUoZ2VuZWFubm90YXRpb24xJGV4dGVybmFsX2dlbmVfbmFtZSkpCgoKYGBgCgpHZW5lcmF0ZSBhIGhlYXRtYXAgd2l0aCBBSVRMLCBQVENMLU5PUyB3aXRoIHRoZSBleHRyYWN0ZWQgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLgoKYGBge3IgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA4LCB3YXJuaW5nPUZBTFNFfQpnZXA8LSBnZW5lRXhwclssc2VsZWN0X2hpc3Qkc2FtcGxlLm5hbWVORVddCm1hdDwtIGdlcFtsaXN0X2dlbmVzLF0KCnNldGRpZmYocm93bmFtZXMobWF0KSwgcGFzdGUwKHVuaXF1ZShnZW5lYW5ub3RhdGlvbjEkZW50cmV6Z2VuZSksIl9hdCIpKQoKZm9yIChpaSBpbiAxOm5yb3cobWF0KSkgewogICNpZihsZW5ndGggKHdoaWNoIChwYXN0ZTAodW5pcXVlKGdlbmVhbm5vdGF0aW9uMSRlbnRyZXpnZW5lKSwiX2F0IikgPT0gcm93bmFtZXMobWF0KVtpaV0pKSAhPSAwICkgcm93bmFtZXMobWF0KSBbaWldID0gZ2VuZWFubm90YXRpb24xJGV4dGVybmFsX2dlbmVfbmFtZSBbIHdoaWNoIChwYXN0ZTAodW5pcXVlKGdlbmVhbm5vdGF0aW9uMSRlbnRyZXpnZW5lKSwiX2F0IikgPT0gcm93bmFtZXMobWF0KVtpaV0pXQogIHJvd25hbWVzKG1hdCkgW2lpXSA9IHVuaXF1ZShnZW5lYW5ub3RhdGlvbjEkZXh0ZXJuYWxfZ2VuZV9uYW1lKSBbIHdoaWNoIChwYXN0ZTAodW5pcXVlKGdlbmVhbm5vdGF0aW9uMSRlbnRyZXpnZW5lKSwiX2F0IikgPT0gcm93bmFtZXMobWF0KVtpaV0pXQp9CgpteWNvbD0gYygicmVkIiwid2hpdGUiLCJ5ZWxsb3ciKQpteWxhYmVsID0gc2VsZWN0X2hpc3RbLGMoInNhbXBsZS5uYW1lTkVXIiwiZmluYWwubW9sZWMiLCJJREgyIiwiUkhPQSIsIlRFVDIiLCJETk1UM0EiKV0Kcm93bmFtZXMobXlsYWJlbCkgPSBteWxhYmVsJHNhbXBsZS5uYW1lTkVXCm15bGFiZWwkc2FtcGxlLm5hbWVORVcgPSBOVUxMCm15bGFiZWwubm9jb2wgPSBteWxhYmVsCm15bGFiZWwuY29sID0gbXlsYWJlbApteWxhYmVsLmNvbFtpcy5uYShteWxhYmVsLmNvbCldPC0wCiNoZWFkKG15bGFiZWwuY29sKQpteWxhYmVsLmNvbCRmaW5hbC5tb2xlY1tteWxhYmVsLmNvbCRmaW5hbC5tb2xlYyA9PSAiQUlUTCJdID0gImJsYWNrIjsgbXlsYWJlbC5jb2wkZmluYWwubW9sZWNbbXlsYWJlbC5jb2wkZmluYWwubW9sZWMgPT0gIlBUQ0wubm9zIl0gPSAib3JhbmdlIgpmb3IgKGEgaW4gMjo1KSBteWxhYmVsLmNvbFssYV0gPSBmYWN0b3IobXlsYWJlbC5jb2xbLGFdLCBsZXZlbHMgPSBsZXZlbHMoYXMuZmFjdG9yKG15bGFiZWwuY29sWyxhXSkpLCBsYWJlbHMgPSBteWNvbCApCgptYXQgIDwtIG1hdCAtIHJvd01lYW5zKG1hdCkKcGFyKG1mcm93PWMoMSwxKSkKY2x1c3Rlci5wdHMubnIgPSBwaGVhdG1hcChtYXQsIGFubm90YXRpb25fY29sID0gbXlsYWJlbC5ub2NvbCwgYW5ub3RhdGlvbl9jb2xvcnMgPSBsaXN0KGZpbmFsLm1vbGVjID0gYyhBSVRMID0gImJsYWNrIiwgUFRDTC5ub3MgPSAib3JhbmdlIiksIGZpbGVuYW1lPSAieC5wZGYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSURIMiA9IGMoTVVUPW15Y29sWzFdLCJOQSI9bXljb2xbMl0sV1Q9bXljb2xbM10pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUkhPQSA9IGMoTVVUPW15Y29sWzFdLCJOQSI9bXljb2xbMl0sV1Q9bXljb2xbM10pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVEVUMiA9IGMoTVVUPW15Y29sWzFdLCJOQSI9bXljb2xbMl0sV1Q9bXljb2xbM10pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRE5NVDNBID0gYyhNVVQ9bXljb2xbMV0sIk5BIj1teWNvbFsyXSxXVD1teWNvbFszXSkgKSAsIHNob3dfY29sbmFtZXMgPSBGLCBjZWxsaGVpZ2h0ID0gMTUsIAogICAgICAgICBib3JkZXJfY29sb3I9IE5BLCBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUocmV2KGJyZXdlci5wYWwobiA9IDUgLCBuYW1lID0gIlJkWWxCdSIpKSkoMjApLCBzY2FsZSA9ICJyb3ciLCBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIixjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiZXVjbGlkZWFuIiAsIHNpbGVudCA9IEYpCgojIyMgZXhwb3J0IHB0cyBvcmRlcgpjbHVzdGVyLnB0cy5uciR0cmVlX2NvbCRsYWJlbHMgW2NsdXN0ZXIucHRzLm5yJHRyZWVfY29sJG9yZGVyXQpjbHVzdGVyLnB0cy5uciR0cmVlX2NvbCRsYWJlbHMgCiNwaGVhdG1hcDo6cGhlYXRtYXAodGVzdCwgZmlsZW5hbWU9InRlc3QucGRmIikKYGBgCgoKTE9PQ1Ygb24gQUlMVCwgUFRDTG5vcyBiYXNlZCBvbiAxNi1nZW5lIG1vZGVsCgpgYGB7cn0KeSA9IHQobWF0KQpjbC5vcmlnID0gYygpCmZvciAodSBpbiAxOm5yb3coeSkpIGNsLm9yaWcgW3VdID0gdW5saXN0KHN0cnNwbGl0KHJvd25hbWVzKHkpW3VdLCJcXC4iKSlbMV0KCnBlcm0ubW90aGVyID0gcm93bmFtZXMoeSkKcGVybS5zb24gPSBjb21ibiAocGVybS5tb3RoZXIsIGxlbmd0aChwZXJtLm1vdGhlciktMSkKCm91dHB1dCA8LSBjYmluZChwZXJtLm1vdGhlciwgTkEpCgpmb3IgKGkgaW4gMTpsZW5ndGgocGVybS5tb3RoZXIpKSB7CiAgdHJhaW4gPC0geSBbIHBlcm0uc29uWyxpXSwgXQogIHRlc3QgPC0geSBbICEgKCByb3duYW1lcyh5KSAlaW4lIHBlcm0uc29uWyxpXSkgLCBdCiAgY2wgPC0gY2wub3JpZyBbd2hpY2gocm93bmFtZXMoeSklaW4lcGVybS5zb25bLGldKV0KICB6IDwtIGxkYSh0cmFpbiwgY2wpCiAgcCA8LSBwcmVkaWN0KHosdGVzdCkkY2xhc3MKICBvdXRwdXQgIFsgc2V0ZGlmZigxOjI3MSwgd2hpY2goIHJvd25hbWVzKHkpICVpbiUgcGVybS5zb25bLGldKSApICwgMiAgXSA9IGFzLmNoYXJhY3RlcihwKQojICBvdXRwdXQgIFsgb3V0cHV0WywxXSA9PSByb3duYW1lcyh0ZXN0KSAsIDMgIF0gPSB6JHNjYWxpbmcgWzEsMV0KIyAgb3V0cHV0ICBbIG91dHB1dFssMV0gPT0gcm93bmFtZXModGVzdCkgLCA0ICBdID0geiRzY2FsaW5nIFsyLDFdCiMgIG91dHB1dCAgWyBvdXRwdXRbLDFdID09IHJvd25hbWVzKHRlc3QpICwgNSAgXSA9IHokc2NhbGluZyBbMywxXQp9Cgpjb2xuYW1lcyhvdXRwdXQpID0gYygidHJ1ZSIsIkxPT0NWLnByZWRpY3RlZCIpCm91dHB1dCA9IGFzLmRhdGEuZnJhbWUob3V0cHV0KQpvdXRwdXQkdHJ1ZS5jbGFzcyA9IGNsLm9yaWcKCnRhYmxlKG91dHB1dCR0cnVlLmNsYXNzLCBvdXRwdXQkTE9PQ1YucHJlZGljdGVkICApCmNvbmZ1c2lvbk1hdHJpeCh0YWJsZShvdXRwdXQkdHJ1ZS5jbGFzcywgb3V0cHV0JExPT0NWLnByZWRpY3RlZCAgKSkKCiMgQ29uZnVzaW9uIE1hdHJpeCBhbmQgU3RhdGlzdGljcwojIAojICAgICAgIAojICAgICAgICBBSVRMIFBUQ0wKIyAgIEFJVEwgIDEwNiAgIDIxCiMgICBQVENMICAgMTYgIDEyOAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiMgICAgICAgICAgICAgICAgQWNjdXJhY3kgOiAwLjg2MzUgICAgICAgICAKIyAgICAgICAgICAgICAgICAgIDk1JSBDSSA6ICgwLjgxNjgsIDAuOTAyKQojICAgICBObyBJbmZvcm1hdGlvbiBSYXRlIDogMC41NDk4ICAgICAgICAgCiMgICAgIFAtVmFsdWUgW0FjYyA+IE5JUl0gOiA8MmUtMTYgICAgICAgICAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAojICAgICAgICAgICAgICAgICAgIEthcHBhIDogMC43MjUyICAgICAgICAgCiMgIE1jbmVtYXIncyBUZXN0IFAtVmFsdWUgOiAwLjUxMDggICAgICAgICAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAojICAgICAgICAgICAgIFNwZWNpZmljaXR5IDogMC44NTkxICAgICAgICAgCiMgICAgICAgICAgUG9zIFByZWQgVmFsdWUgOiAwLjgzNDYgICAgICAgICAKIyAgICAgICAgICBOZWcgUHJlZCBWYWx1ZSA6IDAuODg4OSAgICAgICAgIAojICAgICAgICAgICAgICBQcmV2YWxlbmNlIDogMC40NTAyICAgICAgICAgCiMgICAgICAgICAgRGV0ZWN0aW9uIFJhdGUgOiAwLjM5MTEgICAgICAgICAKIyAgICBEZXRlY3Rpb24gUHJldmFsZW5jZSA6IDAuNDY4NiAgICAgICAgIAojICAgICAgIEJhbGFuY2VkIEFjY3VyYWN5IDogMC44NjQwICAgICAgICAgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKIyAgICAgICAgJ1Bvc2l0aXZlJyBDbGFzcyA6IEFJVEwgICAgICAKCmBgYAoKVXNlIENvbnNlbnN1c0NsdXN0ZXJQbHVzIHRvIGV4dHJhY3QgbW9zdCBzaWduaWZpY2FudCBjbHVzdGVyczogYW5hbHl6ZSBzYW1wbGUgc3RyYXRpZmljYXRpb24gYmFzZWQgb24gdGhlIGV4dHJhY3RlZCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgYmV0d2VlIEFJTFQgYW5kIFBUQ0wtbm9zIGFuZCB0aGUgQUxDTCBBTEstbmVnYXRpdmUgMy1nZW5lIG1vZGVsLgoKYGBge3J9CnNlbGVjdF9oaXN0PC0gcHRzLmluZm8uZGF0YVtwdHMuaW5mby5kYXRhJGZpbmFsLm1vbGVjID09ICJBSVRMIiB8IHB0cy5pbmZvLmRhdGEkZmluYWwubW9sZWMgPT0gIlBUQ0wubm9zIiB8IHB0cy5pbmZvLmRhdGEkZmluYWwubW9sZWMgPT0gIkFMQ0wubmVnIixdCiMgQWRkIHRocmVlIGNsYXNzaWZpZXIgZ2VuZXMgZm9yIEFMQ0wgQUxLLW5lZyBbQWduZWxsaSBldCBhbCwgQmxvb2QsIDIwMTJdCiMgQ2hlY2sgb24gYXJyYXkKYW5hcGxhc3RpY19nZW5lPC0gYygiVE5GUlNGOCIsIkJBVEYzIiwiVE1PRDEiKQpnZW5lYW5ub3RhdGlvbjIgPC0gZ2V0Qk0oIGF0dHJpYnV0ZXMgPSBjKCJlbnRyZXpnZW5lIiwgImV4dGVybmFsX2dlbmVfbmFtZSIpLCBmaWx0ZXJzID0gImV4dGVybmFsX2dlbmVfbmFtZSIsIHZhbHVlcyA9IGFuYXBsYXN0aWNfZ2VuZSwgbWFydCA9IGVuc2VtYmwgKQoKYW5hcGxhc3RpY19nZW5lX0FSUkFZPC0gcGFzdGUwKGdlbmVhbm5vdGF0aW9uMiRlbnRyZXpnZW5lLCAiX2F0IikKCiMgQXBwZW5kIDE2LWdlbmUgbW9kZWwgdG8gMy1nZW5lIG1vZGVsCmxpc3RfZ2VuZXNfYWxsPC0gYyhsaXN0X2dlbmVzLCBhbmFwbGFzdGljX2dlbmVfQVJSQVkpCgojIFJlZG8gY29uc2Vuc3VzIGNsdXN0ZXIgYW5hbHlzaXMKZ2VwPC0gZ2VuZUV4cHJbLHNlbGVjdF9oaXN0JHNhbXBsZS5uYW1lTkVXXQptYXQ8LSBnZXBbbGlzdF9nZW5lc19hbGwsXQp0aXRsZT10ZW1wZGlyKCkKZDwtIGRhdGEubWF0cml4KG1hdCkKZCA9IHN3ZWVwKGQsMSwgYXBwbHkoZCwxLG1lZGlhbixuYS5ybT1UKSkKcmVzdWx0cyA9IENvbnNlbnN1c0NsdXN0ZXJQbHVzKGQsbWF4Sz04LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcEZlYXR1cmU9MSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlPXRpdGxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3RlckFsZz0iaGMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5uZXJMaW5rYWdlPSJ3YXJkLkQyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbmFsTGlua2FnZT0id2FyZC5EMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0YW5jZT0iZXVjbGlkZWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQ9MTIzNDU2Nzg5KQprazwtIGFzLmRhdGEuZnJhbWUoKHJlc3VsdHNbWzVdXSRjb25zZW5zdXNDbGFzcykpICMjIyMjIDQgc2lnbmlmaWNhbnQgY2x1c3RlcgprayRnZW8uaWQ8LSByb3duYW1lcyhraykKY29sbmFtZXMoa2spWzFdPC0gImNsdXN0ZXIiCnRhYmxlKGtrJGNsdXN0ZXIpCmBgYAoKUGxvdCBoZWF0bWFwICBBSVRMLCBQVENMLU5PUywgQUxDTC1uZWcgYW5kIHRoZSAxOS1nZW5lIG1vZGVsCgpgYGB7ciBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDEwfQoKaGVhdDwtIG1lcmdlKHQobWF0KSwga2ssIGJ5LnggPSAwLCBieS55PSJnZW8uaWQiKQpoZWF0MjwtIG1lcmdlKGhlYXQsIHB0cy5pbmZvLmRhdGEsIGJ5LnggPSAxLCBieS55PSJzYW1wbGUubmFtZU5FVyIpCmhlYXQyPC0gaGVhdDJbb3JkZXIoaGVhdDIkY2x1c3RlciksXQpteWNvbD0gYygicmVkIiwid2hpdGUiLCJ5ZWxsb3ciKQpteWxhYmVsID0gaGVhdDJbLGMoIlJvdy5uYW1lcyIsImNsdXN0ZXIiLCJmaW5hbC5tb2xlYyIsIlRFVDIiLCJSSE9BIiwiSURIMiIsIkROTVQzQSIpXQpjb2xuYW1lcyhteWxhYmVsKTwtIGMoInNhbXBsZS5uYW1lcyIsImNsdXN0ZXJzIiwiSGlzdG9sb2d5IiwiVEVUMiIsIlJIT0EiLCJJREgyIiwiRE5NVDNBIikKcm93bmFtZXMobXlsYWJlbCkgPSBteWxhYmVsJHNhbXBsZS5uYW1lcwpteWxhYmVsJHNhbXBsZS5uYW1lcyAgPSBOVUxMCm15bGFiZWwubm9jb2wgPSBteWxhYmVsCm15bGFiZWwuY29sID0gbXlsYWJlbApteWxhYmVsLmNvbFtpcy5uYShteWxhYmVsLmNvbCldPC0wCiNoZWFkKG15bGFiZWwuY29sKQpteWxhYmVsLmNvbCRIaXN0b2xvZ3lbbXlsYWJlbC5jb2wkSGlzdG9sb2d5ID09ICJBSVRMIl0gPSAiYmxhY2siOyBteWxhYmVsLmNvbCRIaXN0b2xvZ3lbbXlsYWJlbC5jb2wkSGlzdG9sb2d5ID09ICJQVENMLm5vcyJdID0gIm9yYW5nZSI7IG15bGFiZWwuY29sJEhpc3RvbG9neVtteWxhYmVsLmNvbCRIaXN0b2xvZ3kgPT0gIkFMQ0wubmVnIl0gPSAieWVsbG93Igpmb3IgKGEgaW4gYygzOjYpKSBteWxhYmVsLmNvbFssYV0gPSBmYWN0b3IobXlsYWJlbC5jb2xbLGFdLCBsZXZlbHMgPSBsZXZlbHMoYXMuZmFjdG9yKG15bGFiZWwuY29sWyxhXSkpLCBsYWJlbHMgPSBteWNvbCApCm15Y29sX3BsdXM8LSBjKGJyZXdlci5wYWwoMTEsIlBhaXJlZCIpLGJyZXdlci5wYWwoNiwiU2V0MiIpKQpmb3IgKGEgaW4gMSkgbXlsYWJlbC5jb2xbLGFdID0gZmFjdG9yKG15bGFiZWwuY29sWyxhXSwgbGV2ZWxzID0gbGV2ZWxzKGFzLmZhY3RvcihteWxhYmVsLmNvbFssYV0pKSwgbGFiZWxzID0gbXljb2xfcGx1c1sxOjVdICkKbXlsYWJlbC5ub2NvbCRjbHVzdGVyczwtYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIobXlsYWJlbC5ub2NvbCRjbHVzdGVycykpCm15bGFiZWwubm9jb2wkY2x1c3RlcnM8LWFzLmNoYXJhY3RlcihwYXN0ZSgiY2x1c3RlciIsbXlsYWJlbC5ub2NvbCRjbHVzdGVycywgc2VwPSIiKSkKICAKcGFyKG1mcm93PWMoMSwxKSkKcGFyKG1hcj1jKDUsNSw1LDUpLCB4cGQ9RikKbWF0MzwtIHQoZGF0YS5tYXRyaXgoaGVhdDJbLDI6MjBdKSkKY29sbmFtZXMobWF0Myk8LWhlYXQyJFJvdy5uYW1lcwptYXQzPSBtYXQzW29yZGVyKHJvd25hbWVzKG1hdDMpKSxdCnRlbXBfbmFtZSA9IGdldEJNKCBhdHRyaWJ1dGVzID0gYygiZW5zZW1ibF90cmFuc2NyaXB0X2lkIiwgImVudHJlemdlbmUiLCAiZXh0ZXJuYWxfZ2VuZV9uYW1lIiksIGZpbHRlcnMgPSAiZW50cmV6Z2VuZSIsIHZhbHVlcyA9IGdzdWIoIl9hdCIsIiIscm93bmFtZXMobWF0MykpLCBtYXJ0ID0gZW5zZW1ibClbLGMoMjozKV0KdGVtcF9uYW1lID0gdGVtcF9uYW1lWyFkdXBsaWNhdGVkKHRlbXBfbmFtZVssMV0pLF0Kcm93bmFtZXMobWF0MykgPSB0ZW1wX25hbWUkZXh0ZXJuYWxfZ2VuZV9uYW1lCm1hdDMgIDwtIG1hdDMgLSByb3dNZWFucyhtYXQzKQpwYXIobWZyb3c9YygxLDEpKQojcGhlYXRtYXAobWF0MywgYW5ub3RhdGlvbl9jb2wgPSBteWxhYmVsLm5vY29sLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoY2x1c3RlcnMgPSBjKGNsdXN0ZXIxPSBteWNvbF9wbHVzWzFdLCBjbHVzdGVyMiA9IG15Y29sX3BsdXNbMl0sIGNsdXN0ZXIzID0gbXljb2xfcGx1c1szXSwgY2x1c3RlcjQgPSBteWNvbF9wbHVzWzRdLCBjbHVzdGVyNSA9IG15Y29sX3BsdXNbNV0pLCBIaXN0b2xvZ3kgPSBjKEFJVEwgPSAiYmxhY2siLCBQVENMLm5vcyA9ICJvcmFuZ2UiLCBBTENMLm5lZz0gInllbGxvdyIpLCBJREgyID0gYyhNVVQ9bXljb2xbMV0sICJOQSI9bXljb2xbMl0sV1Q9bXljb2xbM10pLCBSSE9BID0gYyhNVVQ9bXljb2xbMV0sIk5BIj1teWNvbFsyXSxXVD1teWNvbFszXSksIFRFVDIgPSBjKE1VVD1teWNvbFsxXSwiTkEiPW15Y29sWzJdLFdUPW15Y29sWzNdKSwgRE5NVDNBID0gYyhNVVQ9bXljb2xbMV0sIk5BIj1teWNvbFsyXSxXVD1teWNvbFszXSkgKSAsICBib3JkZXJfY29sb3I9IE5BLCBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUocmV2KGJyZXdlci5wYWwobiA9IDUgLCBuYW1lID0gIlJkWWxCdSIpKSkoMTAwKSwgc2NhbGUgPSAicm93IiwgY2x1c3Rlcl9jb2xzID0gRkFMU0UsIHNob3dfY29sbmFtZXM9IEYsIHJvd19hbm5vdGF0aW9uID0zLCBjZWxsaGVpZ2h0ID0gMjApCiNkZXYub2ZmKCkKIyBwcmludCB3aXRoIGdhcHMKbnVtX2NsdXN0PC0gYXMubnVtZXJpYyh0YWJsZShteWxhYmVsLm5vY29sJGNsdXN0ZXJzKSkKbnVtPC0gYyhudW1fY2x1c3RbMV0sIHN1bShudW1fY2x1c3RbMToyXSksc3VtKG51bV9jbHVzdFsxOjNdKSxzdW0obnVtX2NsdXN0WzE6NF0pLHN1bShudW1fY2x1c3RbMTo1XSkgKQpwYXIobWZyb3c9YygxLDEpKQpwaGVhdG1hcChtYXQzLCBhbm5vdGF0aW9uX2NvbCA9IG15bGFiZWwubm9jb2wsIGFubm90YXRpb25fY29sb3JzID0gbGlzdChjbHVzdGVycyA9IGMoY2x1c3RlcjE9IG15Y29sX3BsdXNbMV0sIGNsdXN0ZXIyID0gbXljb2xfcGx1c1syXSwgY2x1c3RlcjMgPSBteWNvbF9wbHVzWzNdLCBjbHVzdGVyNCA9IG15Y29sX3BsdXNbNF0sIGNsdXN0ZXI1ID0gbXljb2xfcGx1c1s1XSksIEhpc3RvbG9neSA9IGMoQUlUTCA9ICJibGFjayIsIFBUQ0wubm9zID0gIm9yYW5nZSIsIEFMQ0wubmVnPSAieWVsbG93IiksIElESDIgPSBjKE1VVD1teWNvbFsxXSwgIk5BIj1teWNvbFsyXSxXVD1teWNvbFszXSksIFJIT0EgPSBjKE1VVD1teWNvbFsxXSwiTkEiPW15Y29sWzJdLFdUPW15Y29sWzNdKSwgVEVUMiA9IGMoTVVUPW15Y29sWzFdLCJOQSI9bXljb2xbMl0sV1Q9bXljb2xbM10pLCBETk1UM0EgPSBjKE1VVD1teWNvbFsxXSwiTkEiPW15Y29sWzJdLFdUPW15Y29sWzNdKSApICwgIGJvcmRlcl9jb2xvcj0gTkEsIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShyZXYoYnJld2VyLnBhbChuID0gNSAsIG5hbWUgPSAiUmRZbEJ1IikpKSgxMDApLCBzY2FsZSA9ICJyb3ciLCBjbHVzdGVyX2NvbHMgPSBGQUxTRSwgc2hvd19jb2xuYW1lcz0gRiwgcm93X2Fubm90YXRpb24gPTMsIGNlbGxoZWlnaHQgPSAyMCwgIGdhcHNfY29sID0gbnVtKQojIGdhcHNfY29sPWMoMCxyZXAoMCxudW1bMV0tMSksIDQwLHJlcCgwLG51bVsyXS0xKSwgMTAwMCxyZXAoMCxudW1bM10tMSksIDQwLHJlcCgwLG51bVs0XS0xKSwgNDAscmVwKDAsbnVtWzVdLTEpKSkKICAgICAgICAKCmdlcDwtIGdlbmVFeHByWyxzZWxlY3RfaGlzdCRzYW1wbGUubmFtZU5FV10KbWF0PC0gZ2VwW2xpc3RfZ2VuZXNfYWxsLF0KZ2VuZWFubm90YXRpb24xIDwtIGdldEJNKCBhdHRyaWJ1dGVzID0gYygiZW5zZW1ibF90cmFuc2NyaXB0X2lkIiwgImVudHJlemdlbmUiLCAiZXh0ZXJuYWxfZ2VuZV9uYW1lIiksIGZpbHRlcnMgPSAiZW50cmV6Z2VuZSIsIHZhbHVlcyA9IGdzdWIoIl9hdCIsIiIsbGlzdF9nZW5lc19hbGwpLCBtYXJ0ID0gZW5zZW1ibCkKc29ydCh1bmlxdWUoZ2VuZWFubm90YXRpb24xJGV4dGVybmFsX2dlbmVfbmFtZSkpCnNldGRpZmYocm93bmFtZXMobWF0KSwgcGFzdGUwKHVuaXF1ZShnZW5lYW5ub3RhdGlvbjEkZW50cmV6Z2VuZSksIl9hdCIpKQoKZm9yIChpaSBpbiAxOm5yb3cobWF0KSkgewogICNpZihsZW5ndGggKHdoaWNoIChwYXN0ZTAodW5pcXVlKGdlbmVhbm5vdGF0aW9uMSRlbnRyZXpnZW5lKSwiX2F0IikgPT0gcm93bmFtZXMobWF0KVtpaV0pKSAhPSAwICkgcm93bmFtZXMobWF0KSBbaWldID0gZ2VuZWFubm90YXRpb24xJGV4dGVybmFsX2dlbmVfbmFtZSBbIHdoaWNoIChwYXN0ZTAodW5pcXVlKGdlbmVhbm5vdGF0aW9uMSRlbnRyZXpnZW5lKSwiX2F0IikgPT0gcm93bmFtZXMobWF0KVtpaV0pXQogIHJvd25hbWVzKG1hdCkgW2lpXSA9IHVuaXF1ZShnZW5lYW5ub3RhdGlvbjEkZXh0ZXJuYWxfZ2VuZV9uYW1lKSBbIHdoaWNoIChwYXN0ZTAodW5pcXVlKGdlbmVhbm5vdGF0aW9uMSRlbnRyZXpnZW5lKSwiX2F0IikgPT0gcm93bmFtZXMobWF0KVtpaV0pXQp9CgpteWNvbD0gYygicmVkIiwid2hpdGUiLCJ5ZWxsb3ciKQpteWxhYmVsID0gc2VsZWN0X2hpc3RbLGMoInNhbXBsZS5uYW1lTkVXIiwiZmluYWwubW9sZWMiLCJJREgyIiwiUkhPQSIsIlRFVDIiLCJETk1UM0EiKV0Kcm93bmFtZXMobXlsYWJlbCkgPSBteWxhYmVsJHNhbXBsZS5uYW1lTkVXCm15bGFiZWwkc2FtcGxlLm5hbWVORVcgPSBOVUxMCm15bGFiZWwubm9jb2wgPSBteWxhYmVsCm15bGFiZWwuY29sID0gbXlsYWJlbApteWxhYmVsLmNvbFtpcy5uYShteWxhYmVsLmNvbCldPC0wCiNoZWFkKG15bGFiZWwuY29sKQpteWxhYmVsLmNvbCRmaW5hbC5tb2xlY1tteWxhYmVsLmNvbCRmaW5hbC5tb2xlYyA9PSAiQUlUTCJdID0gImJsYWNrIjsgbXlsYWJlbC5jb2wkZmluYWwubW9sZWNbbXlsYWJlbC5jb2wkZmluYWwubW9sZWMgPT0gIlBUQ0wubm9zIl0gPSAib3JhbmdlIjsgbXlsYWJlbC5jb2wkZmluYWwubW9sZWNbbXlsYWJlbC5jb2wkZmluYWwubW9sZWMgPT0gIkFMQ0wubmVnIl0gPSAieWVsbG93Igpmb3IgKGEgaW4gMjo1KSBteWxhYmVsLmNvbFssYV0gPSBmYWN0b3IobXlsYWJlbC5jb2xbLGFdLCBsZXZlbHMgPSBsZXZlbHMoYXMuZmFjdG9yKG15bGFiZWwuY29sWyxhXSkpLCBsYWJlbHMgPSBteWNvbCApCgptYXQgIDwtIG1hdCAtIHJvd01lYW5zKG1hdCkKcGFyKG1mcm93PWMoMSwxKSkKcGhlYXRtYXAobWF0LCBhbm5vdGF0aW9uX2NvbCA9IG15bGFiZWwubm9jb2wsIGFubm90YXRpb25fY29sb3JzID0gbGlzdChmaW5hbC5tb2xlYyA9IGMoQUlUTCA9ICJibGFjayIsIFBUQ0wubm9zID0gIm9yYW5nZSIsIEFMQ0wubmVnID0gInllbGxvdyIpLCBmaWxlbmFtZT0gIngucGRmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElESDIgPSBjKE1VVD1teWNvbFsxXSwiTkEiPW15Y29sWzJdLFdUPW15Y29sWzNdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJIT0EgPSBjKE1VVD1teWNvbFsxXSwiTkEiPW15Y29sWzJdLFdUPW15Y29sWzNdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRFVDIgPSBjKE1VVD1teWNvbFsxXSwiTkEiPW15Y29sWzJdLFdUPW15Y29sWzNdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEROTVQzQSA9IGMoTVVUPW15Y29sWzFdLCJOQSI9bXljb2xbMl0sV1Q9bXljb2xbM10pICkgLCBzaG93X2NvbG5hbWVzID0gRiwgY2VsbGhlaWdodCA9IDE1LCAKICAgICAgICAgYm9yZGVyX2NvbG9yPSBOQSwgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKHJldihicmV3ZXIucGFsKG4gPSA1ICwgbmFtZSA9ICJSZFlsQnUiKSkpKDIwKSwgc2NhbGUgPSAicm93IiwgY2x1c3RlcmluZ19tZXRob2QgPSAid2FyZC5EMiIsY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImV1Y2xpZGVhbiIgLCBzaWxlbnQgPSBGKQoKIyMjIGV4cG9ydCBwdHMgb3JkZXIKY2x1c3Rlci5wdHMubnIkdHJlZV9jb2wkbGFiZWxzIFtjbHVzdGVyLnB0cy5uciR0cmVlX2NvbCRvcmRlcl0KCmBgYAoKIyMgQ2liZXJzb3J0IGFsZ29yaXRobSB0byBjaGFyYWN0ZXJpemUgdGhlIHR1bW91ciBhbmQgbWljcm9lbnZpcm9tZW50IGNvbXBvc2l0aW9uIG9mIGVhY2ggY2x1c3RlcgoKYGBge3IgZmlnLndpZHRoID0gMjAsIGZpZy5oZWlnaHQgPSA3LCB3YXJuaW5nPUZ9CgojIyMjIyBjaWJlcnNvcnQgYW5kIG9yaWdpY2FsIG1vbGVjdWxhciBoaXN0b2xvZ2llcwpsb2FkKCIuL1JtZC5maWxlcy9jaWJlcnNvcnQuYWxsLlJkYXRhIikKY2liZXJfYWxsPC1hcy5kYXRhLmZyYW1lLm1hdHJpeCh0KGNpYmVyc29ydC5wZXJjZW50YWdlcykpCmNpYmVyX2FsbCRzYW1wbGUubmFtZU5FVyA8LSByb3duYW1lcyhjaWJlcl9hbGwpCmNvbG5hbWVzKGtrKVsyXTwtInNhbXBsZS5uYW1lTkVXIgpyZXF1aXJlKHBseXIpCmZpbmFsIDwtam9pbihjaWJlcl9hbGwsIGtrLCBieSA9ICJzYW1wbGUubmFtZU5FVyIsICB0eXBlPSJsZWZ0IikKZmluYWwyPC1tZXJnZShwdHMuaW5mby5kYXRhWyxjKDEsNiwxNDoxNyldLCBmaW5hbCwgYnk9InNhbXBsZS5uYW1lTkVXIikKZmluYWwzPC0gc3Vic2V0KGZpbmFsMiwgZmluYWwubW9sZWMgJWluJSBjKCJBSVRMIiwiQUxDTC5uZWciLCJBTENMLnBvcyIsIkFUTEwiLCJOS1QiLCJQVENMLm5vcyIpKQpmaW5hbDM8LSBmaW5hbDNbb3JkZXIoZmluYWwzJGZpbmFsLm1vbGVjKSxdCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpuIDwtIDIyCnF1YWxfY29sX3BhbHMgPSBicmV3ZXIucGFsLmluZm9bYnJld2VyLnBhbC5pbmZvJGNhdGVnb3J5ID09ICdxdWFsJyxdCmNvbF92ZWN0b3IgPSB1bmxpc3QobWFwcGx5KGJyZXdlci5wYWwsIHF1YWxfY29sX3BhbHMkbWF4Y29sb3JzLCByb3duYW1lcyhxdWFsX2NvbF9wYWxzKSkpCgpwYXIobWFyPWMoMiw1LDcsMTApLCB4cGQ9VFJVRSkKeDwtIGJhcnBsb3QodChmaW5hbDNbNzoyOF0pLCBuYW1lcy5hcmcgPSByZXAoIiIsIGxlbmd0aChmaW5hbDMkZmluYWwubW9sZWMpKSwgY2V4Lm5hbWVzID0gMC43LCBjb2w9Y29sX3ZlY3RvciwgYm9yZGVyPU5BLAogICAgICAgICAgICBzcGFjZT1yZXAoMCwgbnJvdyhmaW5hbDMpKSkKbGVnZW5kKCJ0b3ByaWdodCIsbGVnZW5kPWNvbG5hbWVzKGZpbmFsMylbNzoyOF0sIGNvbD1jb2xfdmVjdG9yLCBwY2g9YygxNSksIGluc2V0PWMoLTAuMTEsMCksIHB0LmNleD0gMSwKY2V4ID0gMSwgYnR5ID0gIm4iLCAgeC5pbnRlcnNwID0gMC43KQoKCm5hbWVzX2hpc3Q8LSB1bmlxdWUoZmluYWwzJGZpbmFsLm1vbGVjKQpjb2xfaGlzdDwtIGMoIm9yYW5nZSIsInllbGxvdyIsImRvZGdlcmJsdWUyIiwiYnJvd24yIiwiZGFya29yY2hpZDEiLCJibGFjayIpCm51bTwtIGFzLm51bWVyaWModGFibGUoZmluYWwzJGZpbmFsLm1vbGVjKSkKZm9yKGkgaW4gKDE6bGVuZ3RoKG51bSkpKQp7CiAgc2VnbWVudHMoeFtzdW0obnVtWzE6aV0pKzEtbnVtW2ldXSwgMS4wNSx4W3N1bShudW1bMTppXSldLDEuMDUsbHdkPTQsIGNvbD1jb2xfaGlzdFtpXSkKICB0ZXh0KHhbKHN1bShudW1bMTppXSktbnVtW2ldICsxKyBzdW0obnVtWzE6aV0pKS8yXSwgMS4xLCBuYW1lc19oaXN0W2ldLCBjZXg9MS4yLCBzcnQ9MCkKCn0KCmBgYAoKCmBgYHtyIGZpZy53aWR0aCA9IDIwLCBmaWcuaGVpZ2h0ID0gNywgd2FybmluZz1GfQoKCiMjIyMjIHBsb3QgY2liZXJzb3J0IHByb2ZpbGUgb2YgcGF0aWVudHMgc3RyYXRpZmllZCBhY2NvcmRpbmcgdG8gaGlzdG9sb2d5IGFuZCBjbHVzdGVycwoKZm9yKGkgaW4gKDE6bnJvdyhmaW5hbDMpKSkKewpmaW5hbDMkY2x1c3RlcltpXVtpcy5uYShmaW5hbDMkY2x1c3RlcltpXSldPC0gZmluYWwzJGZpbmFsLm1vbGVjW2ldCn0KCmZpbmFsMyRjbHVzdGVyIDwtIGZhY3RvcihmaW5hbDMkY2x1c3RlciwgbGV2ZWxzID0gYyggIjEiLCIyIiwiNCIsIk5LVCIsIjMiLCI1IiwiQUxDTC5wb3MiLCAiQVRMTCIpKQoKZmluYWwzPC0gZmluYWwzW29yZGVyKGZpbmFsMyRjbHVzdGVyKSxdCgojcGRmKCJiYXJwbG90X2NpYmVyc29ydC5wZGYiLCB3aWR0aCA9IDIwLCBoZWlnaHQgPSA3KQpwYXIobWFyPWMoMiw1LDcsMTApLCB4cGQ9VFJVRSkKeDwtIGJhcnBsb3QodChmaW5hbDNbNzoyOF0pLCBuYW1lcy5hcmcgPSByZXAoIiIsIGxlbmd0aChmaW5hbDMkZmluYWwubW9sZWMpKSwgY2V4Lm5hbWVzID0gMC43LCBjb2w9Y29sX3ZlY3RvciwgYm9yZGVyPU5BLAogICAgICAgICAgICBzcGFjZT1yZXAoMCwgbnJvdyhmaW5hbDMpKSkKbGVnZW5kKCJ0b3ByaWdodCIsbGVnZW5kPWNvbG5hbWVzKGZpbmFsMylbNzoyOF0sIGNvbD1jb2xfdmVjdG9yLCBwY2g9YygxNSksIGluc2V0PWMoLTAuMTEsMCksIHB0LmNleD0gMSwKY2V4ID0gMSwgYnR5ID0gIm4iLCAgeC5pbnRlcnNwID0gMC43KQoKbXljb2xfcGx1czwtIGMoYnJld2VyLnBhbCgxMSwiUGFpcmVkIiksYnJld2VyLnBhbCg2LCJEYXJrMiIpKQpuYW1lc19oaXN0PC0gYygiQy0xIiwiQy0yIiwgIkMtNCIsIk5LVCIsIkMtMyIsIkMtNSIsIkFMQ0wucG9zIiwiQVRMTCIpCmNvbF9oaXN0PC0gYyhteWNvbF9wbHVzWzFdLG15Y29sX3BsdXNbMl0sbXljb2xfcGx1c1s0XSwiZGFya29yY2hpZDEiLG15Y29sX3BsdXNbM10sbXljb2xfcGx1c1s1XSwiZG9kZ2VyYmx1ZTIiLCJicm93bjIiLCIiKQpudW08LSBhcy5udW1lcmljKHRhYmxlKGZpbmFsMyRjbHVzdGVyKSkKICBwYXIobmV3PVRSVUUpCmZvcihpIGluICgxOihsZW5ndGgobnVtKSkpKQp7CiAgc2VnbWVudHMoeFtzdW0obnVtWzE6aV0pKzEtbnVtW2ldXSwgMS4wNSx4W3N1bShudW1bMTppXSldLDEuMDUsbHdkPTQsIGNvbD1jb2xfaGlzdFtpXSkKICB0ZXh0KHhbKHN1bShudW1bMTppXSktbnVtW2ldICsxKyBzdW0obnVtWzE6aV0pKS8yXSwgMS4xLCBuYW1lc19oaXN0W2ldLCBjZXg9MS4yLCBzcnQ9MCkKCn0KIyBkZXYub2ZmKCkKCmBgYAoKQm94cGxvdCBjb21wYXJpbmcgdGhlIGNvbnRyaWJ1dGlvbiBvZiBlYWNoIGNpYmVyc29ydCBzaWduYXR1cmUgYmV0d2VlbiBhbGwgZXh0cmFjdGVkIGNsdXN0ZXJzCgpgYGB7ciBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDUsIHdhcm5pbmc9Rn0KCnBhcihtZnJvdz1jKDEsMikpCnBhcihtYXI9YygzLDMsMywzKSwgeHBkPUYpCmZvcihpIGluICg3OjI3KSkKewogICNwZGYoc3ByaW50ZigiJXNfY2liZXJzb3J0X3B0Y2wucGRmIixpKSwgaGVpZ2h0PTgsIHdpZHRoPTEwKQogIGs8LSBhcy5udW1lcmljKGZpbmFsMlssaV0pCiAgdGFibGVfd2lsazwtIHBhaXJ3aXNlLndpbGNveC50ZXN0KGssZmluYWwyJGNsdXN0ZXIscC5hZGp1c3QubWV0aG9kcyA9ICJib25mZXJyb25pIiApJHAudmFsdWUKICBkZl93aWxrIDwtIGRhdGEuZnJhbWUoZXhwYW5kLmdyaWQoZGltbmFtZXModGFibGVfd2lsaykpLGFycmF5KHRhYmxlX3dpbGspKQogIGRmX3dpbGsyPC1uYS5vbWl0KGRmX3dpbGspCiAgZGZfd2lsazJfc2lnPC0gZGZfd2lsazJbZGZfd2lsazIkYXJyYXkudGFibGVfd2lsay48MC4wNSxdCiAgZGZfd2lsazJfc2lnJFZhcjE8LWFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRmX3dpbGsyX3NpZyRWYXIxKSkKICBkZl93aWxrMl9zaWckVmFyMjwtYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGZfd2lsazJfc2lnJFZhcjIpKQogIGlmKG5yb3coZGZfd2lsazJfc2lnKT4wKQogIHsKICBib3hwbG90KGt+ZmluYWwyJGNsdXN0ZXIsIHlsaW09YygwLChtYXgoaykrMC4yKSksIG1haW49Y29sbmFtZXMoZmluYWwyKVtpXSwgY2V4Lm1haW49MiwgY29sPW15Y29sX3BsdXMsIGxhcz0yKQogIGZvcihqIGluICgxOm5yb3coZGZfd2lsazJfc2lnKSkpCiAgewogICAgc2VnbWVudHMoZGZfd2lsazJfc2lnJFZhcjFbal0sIG1heChrKS0wLjAxK2ovMzAsIGRmX3dpbGsyX3NpZyRWYXIyW2pdLG1heChrKS0wLjAxK2ovMzApCiAgICBwPC1kZl93aWxrMl9zaWckYXJyYXkudGFibGVfd2lsay5bal0KICAgIGlmKHA8MC4wMDAwMSl7cDIgPSAiPDAuMDAwMDEifWVsc2V7CiAgICBwMjwtYXMubnVtZXJpYyhmb3JtYXRDKHAsZGlnaXRzPTYsZm9ybWF0PSJmIikpfQogICAgcHZhbCA8LSBwYXN0ZSgicCA9IixwMixzZXA9IiAiKQogICAgdGV4dCgoZGZfd2lsazJfc2lnJFZhcjFbal0rIGRmX3dpbGsyX3NpZyRWYXIyW2pdKS8xLjksICBtYXgoaykgK2ovMzAsIHB2YWwsIGNleD0wLjgpCiAgfQogICAgfQogICNkZXYub2ZmKCkgICAKICAgfQoKYGBgCgojIyBSIHRtb2QgcGFja2FnZSBhbmFseXNpcyAKCmBgYHtyIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNSwgd2FybmluZz1GfQoKIyBmb3IgY29udmVuaWVuY2U6IHJlaW1wb3J0IGFubm90YXRlZCBtYXRyaXgKZmluYWw8LSByZWFkLmRlbGltKCIuL1JtZC5maWxlcy9haXRsX25vc19hbGNsX2Nsc3V0ZXJpbmcudHh0IixzZXA9Ilx0IixoZWFkZXIgPSBULHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpmaW5hbDI8LSBmaW5hbFssYygiUm93Lm5hbWVzIiwiaGlzdCIsImNsdXN0ZXIiKV0KbWF0PC0gcmVhZC5kZWxpbSgiLi9SbWQuZmlsZXMvZW5zZW1ibF9hbm5vdGF0ZWRfbWF0cml4LnR4dCIsIHNlcD0iXHQiLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKCmRlc2lnbiA8LSBtb2RlbC5tYXRyaXgofiAwK2ZhY3RvcihmaW5hbDIkY2x1c3RlcikpICMjIyMjIGNyZWF0ZSBtYXRyaXgKY29sbmFtZXMoZGVzaWduKTwtcGFzdGUwKCJDbHVzdGVyXyIsYygxOjUpKQpjb250cmFzdC5tYXRyaXggPC0gbWFrZUNvbnRyYXN0cyhDbHVzdGVyXzItQ2x1c3Rlcl8xLCBDbHVzdGVyXzMtQ2x1c3Rlcl8xLENsdXN0ZXJfNC1DbHVzdGVyXzEsIENsdXN0ZXJfNS1DbHVzdGVyXzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXJfMy1DbHVzdGVyXzIsIENsdXN0ZXJfNC1DbHVzdGVyXzIsIENsdXN0ZXJfNS1DbHVzdGVyXzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXJfNC1DbHVzdGVyXzMsIENsdXN0ZXJfNS1DbHVzdGVyXzMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXJfNC1DbHVzdGVyXzUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXJfMi0oQ2x1c3Rlcl8xICsgQ2x1c3Rlcl8zICsgQ2x1c3Rlcl80ICsgQ2x1c3Rlcl81KS80LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDbHVzdGVyXzMtKENsdXN0ZXJfMSArIENsdXN0ZXJfMiArIENsdXN0ZXJfNCArIENsdXN0ZXJfNSkvNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2x1c3Rlcl80LShDbHVzdGVyXzEgKyBDbHVzdGVyXzIgKyBDbHVzdGVyXzMgKyBDbHVzdGVyXzUpLzQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXJfMS0oQ2x1c3Rlcl8yICsgQ2x1c3Rlcl8zICsgQ2x1c3Rlcl80ICsgQ2x1c3Rlcl81KS80LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDbHVzdGVyXzUtKENsdXN0ZXJfMiArIENsdXN0ZXJfMyArIENsdXN0ZXJfNCArIENsdXN0ZXJfMSkvNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWRlc2lnbikKZml0MSA8LSBsbUZpdChtYXQsIGRlc2lnbikKZml0MiA8LSBjb250cmFzdHMuZml0KGZpdDEsIGNvbnRyYXN0Lm1hdHJpeCkKZml0IDwtIGVCYXllcyhmaXQyKQoKZ2VuZUV4cHIgPSBhZGouZGF0YQpnZW5lRXhwcjI8LSBnZW5lRXhwclssY29sbmFtZXMoZ2VuZUV4cHIpICVpbiUgZmluYWwyJFJvdy5uYW1lcyBdCmdlbmVFeHByMjwtIGdlbmVFeHByMlssZmluYWwyJFJvdy5uYW1lc10KZW5zZW1ibCA9IHVzZU1hcnQoICJlbnNlbWJsIiwgZGF0YXNldCA9ICJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiICkKaGduYyA8LSBnZXRCTShhdHRyaWJ1dGVzPWMoJ2VudHJlemdlbmUnLCdoZ25jX3N5bWJvbCcsJ2hnbmNfaWQnKSxmaWx0ZXJzID0gJ2VudHJlemdlbmUnLCB2YWx1ZXMgPSBnc3ViKCJfYXQiLCIiLHJvd25hbWVzKGdlbmVFeHByMikpLG1hcnQgPSBlbnNlbWJsKQpnZW5lRXhwcjM8LSBhcy5kYXRhLmZyYW1lLm1hdHJpeChnZW5lRXhwcjJbd2hpY2gocm93bmFtZXMoZ2VuZUV4cHIyKSAlaW4lIHBhc3RlMChoZ25jJGVudHJlemdlbmUsIl9hdCIpKSxdKQoKbGV2ZWxzX2Rlc2lnbjwtIGMoIkNsdXN0ZXJfMi1DbHVzdGVyXzEiLCJDbHVzdGVyXzMtQ2x1c3Rlcl8xIiwiQ2x1c3Rlcl80LUNsdXN0ZXJfMSIsIkNsdXN0ZXJfNS1DbHVzdGVyXzEiLAogICAgICAgICAgICAgICAgICJDbHVzdGVyXzMtQ2x1c3Rlcl8yIiwiQ2x1c3Rlcl80LUNsdXN0ZXJfMiIsIkNsdXN0ZXJfNS1DbHVzdGVyXzIiLCJDbHVzdGVyXzQtQ2x1c3Rlcl8zIiwKICAgICAgICAgICAgICAgICAiQ2x1c3Rlcl81LUNsdXN0ZXJfMyIsIkNsdXN0ZXJfNC1DbHVzdGVyXzUiLAogICAgICAgICAgICAgICAgICJDbHVzdGVyXzItKENsdXN0ZXJfMSArIENsdXN0ZXJfMyArIENsdXN0ZXJfNCArIENsdXN0ZXJfNSkvNCIsCiAgICAgICAgICAgICAgICAgIkNsdXN0ZXJfMy0oQ2x1c3Rlcl8xICsgQ2x1c3Rlcl8yICsgQ2x1c3Rlcl80ICsgQ2x1c3Rlcl81KS80IiwKICAgICAgICAgICAgICAgICAiQ2x1c3Rlcl80LShDbHVzdGVyXzEgKyBDbHVzdGVyXzIgKyBDbHVzdGVyXzMgKyBDbHVzdGVyXzUpLzQiLAogICAgICAgICAgICAgICAgICJDbHVzdGVyXzEtKENsdXN0ZXJfMiArIENsdXN0ZXJfMyArIENsdXN0ZXJfNCArIENsdXN0ZXJfNSkvNCIsCiAgICAgICAgICAgICAgICAgIkNsdXN0ZXJfNS0oQ2x1c3Rlcl8yICsgQ2x1c3Rlcl8zICsgQ2x1c3Rlcl80ICsgQ2x1c3Rlcl8xKS80IikKZGZfZGlmZl9hbGw9TlVMTApmb3IoaSBpbiAoMTpsZW5ndGgobGV2ZWxzX2Rlc2lnbikpKQp7CnR0IDwtIHRvcFRhYmxlKGZpdCwgY29lZj1pLCBudW1iZXI9SW5mLCBnZW5lbGlzdD1yb3duYW1lcyhnZW5lRXhwcjMpKQp0dCRJRDwtIHJvd25hbWVzKHR0KQpjb2xuYW1lcyh0dClbMV08LSJHRU5FX1NZTUJPTCIKaGVhZCh0dCwgMTApCmZnIDwtIHR0JEdFTkVfU1lNQk9MW3R0JGFkai5QLlZhbCA8IDAuMDAxICYgYWJzKCB0dCRsb2dGQyApID4gMl0KbGVuZ3RoKGZnKQpkZl9kaWZmPC0gY2JpbmQoZmcsIHJlcChsZXZlbHNfZGVzaWduW2ldLCBsZW5ndGgoZmcpKSkKZGZfZGlmZl9hbGw8LXJiaW5kKGRmX2RpZmZfYWxsLCBkZl9kaWZmKQojcGxvdCh0dCRsb2dGQywgLWxvZzEwKHR0JGFkai5QLlZhbCkpCn0KZGZfZGlmZl9hbGw8LSBhcy5kYXRhLmZyYW1lLm1hdHJpeChkZl9kaWZmX2FsbCkKCmFubm90YXRpb25fY29sPC0gZmluYWwyCmNvbG5hbWVzKGFubm90YXRpb25fY29sKTwtYygic2FtcGxlSUQiLCJIaXN0IiwiY2x1c3RlciIpCkEgPC0gZnVuY3Rpb24oeCkgKGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIoeCkpKSAjIyMjIyBsYXBwbHkgZnVuY3Rpb24gZm9yIGFsbCBjb2x1bW5zIHRvIGdlbmVyYXRlIHRoZSByZWxhdGl2ZSBjb250cmlidXRpb24KYW5ub3RhdGlvbl9jb2xbLDE6bmNvbChhbm5vdGF0aW9uX2NvbCldID0gYXBwbHkoYW5ub3RhdGlvbl9jb2xbLDE6bmNvbChhbm5vdGF0aW9uX2NvbCldLCAyLCBmdW5jdGlvbih4KSBhcy5mYWN0b3IoYXMuY2hhcmFjdGVyKHgpKSkKYW5ub3RhdGlvbl9jb2w8LSBhcy5kYXRhLmZyYW1lKGFubm90YXRpb25fY29sWywtMV0pCm15Y29sX3BsdXM8LSBjKGJyZXdlci5wYWwoMTEsIlBhaXJlZCIpLGJyZXdlci5wYWwoNiwiRGFyazIiKSkKYW5uX2NvbG9ycyA9IGxpc3QoSGlzdD1jKCAiQUlUTCI9ImJsYWNrIiwiQUxDTCI9InllbGxvdyIsIlBUQ0wiPSJvcmFuZ2UiKSwKICAgICAgICAgICAgICAgICAgY2x1c3Rlcj1jKCIxIiA9IG15Y29sX3BsdXNbMV0sIjIiID0gbXljb2xfcGx1c1syXSwiMyIgPSBteWNvbF9wbHVzWzNdLCI0IiA9IG15Y29sX3BsdXNbNF0sIjUiID1teWNvbF9wbHVzWzVdKQogICAgICAgICAgICAgICAgICApCgplZGF0YTM8LSBtYXRbcm93bmFtZXMobWF0KSAlaW4lIHVuaXF1ZShkZl9kaWZmX2FsbCRmZyksXQpwaGVhdG1hcChhcy5tYXRyaXgoIGFzLm1hdHJpeChlZGF0YTMpKSwgYW5ub3RhdGlvbl9jb2w9YW5ub3RhdGlvbl9jb2wsIGFubm90YXRpb25fY29sb3JzID0gYW5uX2NvbG9ycywgYm9yZGVyX2NvbG9yPSJOQSIsIHNjYWxlID0gInJvdyIsIGNsdXN0ZXJfY29scyA9IEZBTFNFLCBzaG93X2NvbG5hbWVzPSBGLCBzaG93X3Jvd25hbWVzID0gRkFMU0UpCgpsZXZlbHNfZGVzaWduPC0gYygiQ2x1c3Rlcl8yLUNsdXN0ZXJfMSIsIkNsdXN0ZXJfMy1DbHVzdGVyXzEiLCJDbHVzdGVyXzQtQ2x1c3Rlcl8xIiwiQ2x1c3Rlcl81LUNsdXN0ZXJfMSIsCiAgICAgICAgICAgICAgICAgIkNsdXN0ZXJfMy1DbHVzdGVyXzIiLCJDbHVzdGVyXzQtQ2x1c3Rlcl8yIiwiQ2x1c3Rlcl81LUNsdXN0ZXJfMiIsIkNsdXN0ZXJfNC1DbHVzdGVyXzMiLAogICAgICAgICAgICAgICAgICJDbHVzdGVyXzUtQ2x1c3Rlcl8zIiwiQ2x1c3Rlcl80LUNsdXN0ZXJfNSIsCiAgICAgICAgICAgICAgICAgIkNsdXN0ZXJfMi0oQ2x1c3Rlcl8xICsgQ2x1c3Rlcl8zICsgQ2x1c3Rlcl80ICsgQ2x1c3Rlcl81KS80IiwKICAgICAgICAgICAgICAgICAiQ2x1c3Rlcl8zLShDbHVzdGVyXzEgKyBDbHVzdGVyXzIgKyBDbHVzdGVyXzQgKyBDbHVzdGVyXzUpLzQiLAogICAgICAgICAgICAgICAgICJDbHVzdGVyXzQtKENsdXN0ZXJfMSArIENsdXN0ZXJfMiArIENsdXN0ZXJfMyArIENsdXN0ZXJfNSkvNCIsCiAgICAgICAgICAgICAgICAgIkNsdXN0ZXJfMS0oQ2x1c3Rlcl8yICsgQ2x1c3Rlcl8zICsgQ2x1c3Rlcl80ICsgQ2x1c3Rlcl81KS80IiwKICAgICAgICAgICAgICAgICAiQ2x1c3Rlcl81LShDbHVzdGVyXzIgKyBDbHVzdGVyXzMgKyBDbHVzdGVyXzQgKyBDbHVzdGVyXzEpLzQiKQpkZl9kaWZmX2FsbD1OVUxMCmZvcihpIGluICgxOmxlbmd0aChsZXZlbHNfZGVzaWduKSkpCnsKdHQgPC0gdG9wVGFibGUoZml0LCBjb2VmPWksIG51bWJlcj1JbmYsIGdlbmVsaXN0PXJvd25hbWVzKGdlbmVFeHByMykpCnR0JElEPC0gcm93bmFtZXModHQpCmNvbG5hbWVzKHR0KVsxXTwtIkdFTkVfU1lNQk9MIgpoZWFkKHR0LCAxMCkKZmcgPC0gdHQkR0VORV9TWU1CT0xbdHQkYWRqLlAuVmFsIDwgMC4wMDEgJiBhYnMoIHR0JGxvZ0ZDICkgPiAyXQpsZW5ndGgoZmcpCmRmX2RpZmY8LSBjYmluZChmZywgcmVwKGxldmVsc19kZXNpZ25baV0sIGxlbmd0aChmZykpKQpkZl9kaWZmX2FsbDwtcmJpbmQoZGZfZGlmZl9hbGwsIGRmX2RpZmYpCiNwbG90KHR0JGxvZ0ZDLCAtbG9nMTAodHQkYWRqLlAuVmFsKSkKfQpkZl9kaWZmX2FsbDwtIGFzLmRhdGEuZnJhbWUubWF0cml4KGRmX2RpZmZfYWxsKQp0YWJsZShkZl9kaWZmX2FsbCRWMikKYW5ub3RhdGlvbl9jb2w8LSBmaW5hbDIKY29sbmFtZXMoYW5ub3RhdGlvbl9jb2wpPC1jKCJzYW1wbGVJRCIsIkhpc3QiLCJjbHVzdGVyIikKQSA8LSBmdW5jdGlvbih4KSAoYXMuZmFjdG9yKGFzLmNoYXJhY3Rlcih4KSkpICMjIyMjIGxhcHBseSBmdW5jdGlvbiBmb3IgYWxsIGNvbHVtbnMgdG8gZ2VuZXJhdGUgdGhlIHJlbGF0aXZlIGNvbnRyaWJ1dGlvbgphbm5vdGF0aW9uX2NvbFssMTpuY29sKGFubm90YXRpb25fY29sKV0gPSBhcHBseShhbm5vdGF0aW9uX2NvbFssMTpuY29sKGFubm90YXRpb25fY29sKV0sIDIsIGZ1bmN0aW9uKHgpIGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIoeCkpKQphbm5vdGF0aW9uX2NvbDwtIGFzLmRhdGEuZnJhbWUoYW5ub3RhdGlvbl9jb2xbLC0xXSkKbXljb2xfcGx1czwtIGMoYnJld2VyLnBhbCgxMSwiUGFpcmVkIiksYnJld2VyLnBhbCg2LCJEYXJrMiIpKQphbm5fY29sb3JzID0gbGlzdChIaXN0PWMoICJBSVRMIj0iYmxhY2siLCJBTENMIj0ieWVsbG93IiwiUFRDTCI9Im9yYW5nZSIpLAogICAgICAgICAgICAgICAgICBjbHVzdGVyPWMoIjEiID0gbXljb2xfcGx1c1sxXSwiMiIgPSBteWNvbF9wbHVzWzJdLCIzIiA9IG15Y29sX3BsdXNbM10sIjQiID0gbXljb2xfcGx1c1s0XSwiNSIgPW15Y29sX3BsdXNbNV0pCiAgICAgICAgICAgICAgICAgICkKZWRhdGEzPC0gbWF0W3Jvd25hbWVzKG1hdCkgJWluJSB1bmlxdWUoZGZfZGlmZl9hbGwkZmcpLF0KcGhlYXRtYXAoYXMubWF0cml4KCBhcy5tYXRyaXgoZWRhdGEzKSksIGFubm90YXRpb25fY29sPWFubm90YXRpb25fY29sLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubl9jb2xvcnMsIGJvcmRlcl9jb2xvcj0iTkEiLAogICAgICAgICBzY2FsZSA9ICJyb3ciLCBjbHVzdGVyX2NvbHMgPSBGQUxTRSwgc2hvd19jb2xuYW1lcz0gRiwgc2hvd19yb3duYW1lcyA9IEZBTFNFKQojIyMjIyMjIyMjIyMgdGFibGUgb2YgZ2VuZXMKZGZfZGlmZl9hbGxfdGFiPU5VTEwKZm9yKGkgaW4gKDE6bGVuZ3RoKGxldmVsc19kZXNpZ24pKSkKewogIHR0IDwtIHRvcFRhYmxlKGZpdCwgY29lZj1pLCBudW1iZXI9SW5mLCBnZW5lbGlzdD1yb3duYW1lcyhnZW5lRXhwcjMpKQogIHR0JElEPC0gcm93bmFtZXModHQpCiAgY29sbmFtZXModHQpWzFdPC0iR0VORV9TWU1CT0wiCiAgaGVhZCh0dCwxMCkKICBmZyA8LSB0dFt0dCRhZGouUC5WYWwgPCAwLjAwMSAmIGFicyggdHQkbG9nRkMgKSA+IDIsXQogIGlmKG5yb3coZmcpPjApewogICAgZmckZGVzaWduPC0gbGV2ZWxzX2Rlc2lnbltpXQogIGRmX2RpZmZfYWxsX3RhYjwtcmJpbmQuZGF0YS5mcmFtZShkZl9kaWZmX2FsbF90YWIsIGZnKQogICNwbG90KHR0JGxvZ0ZDIiwiIC1sb2cxMCh0dCRhZGouUC5WYWwpKQogIH0KICB9Cm5yb3coZGZfZGlmZl9hbGxfdGFiKSAjIyMjIG51bWJlciBvZiBnZW5lcyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmV0d2VlbiBDLTEsIEMtMiwgQy0zLCBDLTQsIEMtNQojIyMjIyBsaXN0IGdlbmUgZnJvbSBJcWJhbCBldCBhbC4gYmxvb2QgMjAxNAppcWJhbDwtIHVuaXF1ZShjKCJFRk5CMiIsIlJPQk8xIiwiUzFQUjMiLCJBTksyIiwiTFBBUjEiLCJTTkFQOTEiLCJTT1g4IiwiTFBBUjEiLCJSQU1QMyIsIlMxUFIzIiwiUk9CTzEiLCJFRk5CMiIsIlRVQkIyQiIsIlNPWDgiLAogICAgICAgICAgICAgICAgICJTT1g4IiwiQVJIR0VGMTAiLCJETVJUMSIsICAiU0xDMTlBMjEiLCJTVEszIiwiUEVSUCIsIlRORlJTRjgiLCJUTU9EMSIsIkJBVEYzIiwiQ0RDMTRCIiwiUEVSUCIsIldERkVZMyIsCiAgICAgICAgICAgICAgICAgIlRNT0QxIiwiQVRQNlYwRDEiLCJBWEwiLCJDRDU5IiwiQ0hJM0wxIiwiQ0xUQyIsIkNPTDZBMSIsIkNSRUcxIiwiQ1RTQiIsIkNUU0MiLCJOUjEiLCJIMyIsIlBEWEsiLCJQSVRQTkEiLAogICAgICAgICAgICAgICAgICJQTFNDUjEiLCJQUkRYMyIsIkNUU1MiLCJDWUJCIiwiRkFCUDMiLCJGUFIxIiwiRlRMIiwiR1VDQTJBIiwiSENLIiwiSUZJMzAiLCJJTDEzUkExIiwiSkFLMiIsIkxJTFJCMSIsCiAgICAgICAgICAgICAgICAgIlBSS0cxUFNBUCIsIlNMQzdBNyIsIlNPRDIiLCJUQ04yIiwiVEhZMSIsIlRZUiIsIlVCRTJMNiIsIldBUlMiLCJBWEwiLCJGVEwiLCJTSVJQQSIsIlNUQVQxIiwiQ1NGMiIsIklGTkciLAogICAgICAgICAgICAgICAgICJTRVBUNiIsIkdBVEEzIiwiQ0QyOCIsIlNUQVQxIiwiQVhMIiwiQ0QyOCIsIkNENDAiLCJDRDU5IiwiQ1NGMiIsIkZUTCIsIklGTkciLCJMSUxSQjEiLCJTSVJQQSIsIlRCWDIxIiwKICAgICAgICAgICAgICAgICAiTVNINiIsIkVHUjEiLCJDQVQiLCJFR1IxIiwiQ0FUIikpCmludGVyc2VjdChpcWJhbCwgdW5pcXVlKGRmX2RpZmZfYWxsX3RhYiRHRU5FX1NZTUJPTCkpCgpgYGAKIyBwYWlyd2lzZSBmb3IgcGF0aHdheSB1c2luZyB0bW9kIChodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdG1vZC92aWduZXR0ZXMvdG1vZC5wZGYpCgpgYGB7ciBmaWcud2lkdGggPSAyMCwgZmlnLmhlaWdodCA9IDIwLCB3YXJuaW5nPUYgfQpmaXQxIDwtIGxtRml0KG1hdCwgZGVzaWduKQpmaXQyIDwtIGNvbnRyYXN0cy5maXQoZml0MSwgY29udHJhc3QubWF0cml4KQpmaXQgPC0gZUJheWVzKGZpdDIpCnJlcy5sIDwtIHRtb2RMaW1tYVRlc3QoZml0LCByb3duYW1lcyhtYXQpKQpsZW5ndGgocmVzLmwpCm5hbWVzKHJlcy5sKQpwaWUgPC0gdG1vZExpbW1hRGVjaWRlVGVzdHMoZml0LCBnZW5lcz1yb3duYW1lcyhtYXQpKQpwYXIobWZyb3c9YygxLDEpKQpyZXMubDI8LSBsYXBwbHkocmVzLmwsIGZ1bmN0aW9uKHgpIHt4W3gkYWRqLlAuVmFsPDEwZS04LF19KQp0bW9kUGFuZWxQbG90KHJlcy5sMiwgcGllPXBpZSwgdGV4dC5jZXg9MC42KSAjIyMjIyB6ZXJvID0gZ3JleSwgYmx1ZSBkb3duIGluIHRoZSBmaXJzdCBmYWN0b3IgYW5kIHJlZCB1cCBpbiB0aGUgZmlyc3QKcmVzLmwyPC0gbGFwcGx5KHJlcy5sLCBmdW5jdGlvbih4KSB7eFt4JGFkai5QLlZhbD4xMGUtOCAmIHgkYWRqLlAuVmFsPDEwZS01LF19KQp0bW9kUGFuZWxQbG90KHJlcy5sMiwgcGllPXBpZSwgdGV4dC5jZXg9MC42KSAjIyMjIyB6ZXJvID0gZ3JleSwgYmx1ZSBkb3duIGluIHRoZSBmaXJzdCBmYWN0b3IgYW5kIHJlZCB1cCBpbiB0aGUgZmlyc3QKYGBgCg==